Convert numbers to SI prefix

前端 未结 5 725
温柔的废话
温柔的废话 2020-12-01 13:01

Is there a R function (or any package) allowing to format numbers (integer) using standard unit prefix (Kilo, Mega etc ...), so

10 -> 10
100 -> 1K
0.01         


        
相关标签:
5条回答
  • 2020-12-01 13:42
    require(sitools)
    f2si(80000)
     [1] "80 k"
    f2si(8E12)
     [1] "8 T"
    

    It seems to be very simplistic as it appends two spaces if no SI prefix is used:

    f2si(80)
    [1] "80  "
    

    The function is easy to modify to include rounding. I also fixed the issue with appended spaces.

    f2si2<-function (number,rounding=F) 
    {
        lut <- c(1e-24, 1e-21, 1e-18, 1e-15, 1e-12, 1e-09, 1e-06, 
            0.001, 1, 1000, 1e+06, 1e+09, 1e+12, 1e+15, 1e+18, 1e+21, 
            1e+24)
        pre <- c("y", "z", "a", "f", "p", "n", "u", "m", "", "k", 
            "M", "G", "T", "P", "E", "Z", "Y")
        ix <- findInterval(number, lut)
        if (lut[ix]!=1) {
            if (rounding==T) {
             sistring <- paste(round(number/lut[ix]), pre[ix])
            }
            else {
             sistring <- paste(number/lut[ix], pre[ix])
            } 
        }
        else {
            sistring <- as.character(number)
        }
        return(sistring)
    }
    
    f2si2(12345)
     [1] "12.345 k"
    f2si2(12345,T)
     [1] "12 k"
    
    0 讨论(0)
  • 2020-12-01 13:44

    This is simple to vectorise using case_when from dplyr, and it's much easier on the eyes:

    library(dplyr)
    
    si_number = function(x, digits) {
    
        compress = function(x, n) {
            signif(x * 10^(-n), digits)
        }
    
        case_when(
            x >= 1e6   ~ paste0(compress(x, 6), "M"),
            x >= 1000  ~ paste0(compress(x, 3), "k"),
            x >= 1     ~ as.character(compress(x, 0)),
            x >= 0.001 ~ paste0(compress(x, -3), "m"),
            x >= 1e-6  ~ paste0(compress(x, -6), "u")
        )
    }
    
    0 讨论(0)
  • 2020-12-01 13:45

    I was looking for Thousand(K), million(M) and Billion(B) number converter. I modified this routine to take a numeric vector/single number spitting out the required output.

    CurrencyFormat <-function (number,rounding=F) 
    {
        first <- TRUE
        lut <- c( 1, 1000, 1000000, 1000000000,1000000000000 )
        pre <- c("", "K", "M", "B", "T")
        if (length(number) > 1) {
            for (cnt in 1:length(number)){        
                ix <- findInterval(number[cnt], lut)
                if (ix != 0 | ix != 1){
                    if (rounding==T) {
                        sistring <- paste(round(number[cnt]/lut[ix]), pre[ix])
                    }
                    else {
                        sistring <- paste(signif(number[cnt]/lut[ix],digits=5), pre[ix])
                    }
                    if (first){
                        tnumber <- sistring
                        fnumber <- tnumber
                        first <- FALSE
                    }
                    else
                        fnumber <- append(fnumber, sistring)
                }
                else {
                    sistring <- number[cnt]
                    if (first){
                        tnumber <- sistring
                        fnumber <- tnumber
                        first <- FALSE
                    }
                    else
                        fnumber <- append(fnumber, sistring)
                }
            }
            return(fnumber)
        }
        else{
            ix <- findInterval(number, lut)
            if (ix != 0 | ix != 1){
                if (rounding==T) {
                    sistring <- paste(round(number/lut[ix]), pre[ix])
                }
                else {
                    sistring <- paste(signif(number/lut[ix],digits=5), pre[ix])
                }
                return(sistring)
            }    
            else
                return(number)
        }
    }
    

    Examples:

    CurrencyFormat(1.25,F)
    [1] "1.25 "
    
    CurrencyFormat(1000.25,F)
    [1] "1.0002 K"
    
    CurrencyFormat(c( 1,45,1234, 4.36e+06, 2.84e+04, 2.01e+06),F)
    [1] "1 "      "45 "     "1.234 K" "4.36 M"  "28.4 K"  "2.01 M" 
    
    0 讨论(0)
  • 2020-12-01 13:47

    I came here with the same question. Thanks to Roland for his answer; I built on his code with a few changes:

    • Allows significant figures to be specified when rounding=FALSE (defaults to 6 just like the 'signif' builtin function)
    • Doesn't throw an error with values below 1e-24
    • Outputs scientific notation (no units) for values above 1e27

    Hope this is helpful.

    f2si<-function (number, rounding=F, digits=ifelse(rounding, NA, 6)) 
    {
        lut <- c(1e-24, 1e-21, 1e-18, 1e-15, 1e-12, 1e-09, 1e-06, 
            0.001, 1, 1000, 1e+06, 1e+09, 1e+12, 1e+15, 1e+18, 1e+21, 
            1e+24, 1e+27)
        pre <- c("y", "z", "a", "f", "p", "n", "u", "m", "", "k", 
            "M", "G", "T", "P", "E", "Z", "Y", NA)
        ix <- findInterval(number, lut)
        if (ix>0 && ix<length(lut) && lut[ix]!=1) {
            if (rounding==T && !is.numeric(digits)) {
                sistring <- paste(round(number/lut[ix]), pre[ix])
            }
            else if (rounding == T || is.numeric(digits)) {
                sistring <- paste(signif(number/lut[ix], digits), pre[ix])
            }
            else {
                sistring <- paste(number/lut[ix], pre[ix])
            } 
        }
        else {
            sistring <- as.character(number)
        }
        return(sistring)
    }
    
    f2si(12345)
     [1] "12.345 k"
    f2si(12345, T)
     [1] "12 k"
    f2si(10^31)
     [1] "1e+31" # (previous version would output "1e+07 Y"
    f2si(10^-25)
     [1] "1e-25" # (previous version would throw error)
    f2si(123456789)
     [1] "123.457 M" # (previous version would output ""123.456789 M"
    f2si(123456789, digits=4)
     [1] "123.5 M" # (note .456 is rounded up to .5)
    

    From this code it's pretty easy to write a similar function for commonly used financial units (K, MM, Bn, Tr), too.

    0 讨论(0)
  • 2020-12-01 13:59

    Slightly modified version to account for negative numbers :

    f2si<-function (number, rounding=F, digits=ifelse(rounding, NA, 6)) 
    {
    mysign <- ""
    if (number<0) {
        mysign <- "-"
    }
    number <- abs(number)
    lut <- c(1e-24, 1e-21, 1e-18, 1e-15, 1e-12, 1e-09, 1e-06, 
        0.001, 1, 1000, 1e+06, 1e+09, 1e+12, 1e+15, 1e+18, 1e+21, 
        1e+24, 1e+27)
    pre <- c("y", "z", "a", "f", "p", "n", "u", "m", "", "k", 
        "M", "G", "T", "P", "E", "Z", "Y", NA)
    ix <- findInterval(number, lut)
    if (ix>0 && ix<length(lut) && lut[ix]!=1) {
        if (rounding==T && !is.numeric(digits)) {
            sistring <- paste(mysign,mysign,round(number/lut[ix]), pre[ix])
        }
        else if (rounding == T || is.numeric(digits)) {
            sistring <- paste(mysign,signif(number/lut[ix], digits), pre[ix],sep="")
        }
        else {
            sistring <- paste(mysign,number/lut[ix], pre[ix],sep="")
        } 
    } else {
        sistring <- paste(mysign,as.character(number),sep="")
    }
    return(sistring)
    

    }

    0 讨论(0)
提交回复
热议问题