Форматирование больших валютных или долларовых значений в миллионы/миллиарды

Мне нужна простая функция или пакет, который будет форматировать:

1 6,000,000
2 75,000,400
3 743,450,000
4 340,000
5 4,300,000

To:

1 6.0 M
2 75.0 M
3 743.5 M
4 0.3 M
5 4.3 M

Или иначе сделать большие значения (миллионы, миллиарды) более разборчивыми для распечатки в таблице.

Ответ 1

Если вы начинаете с этого числового вектора x,

x <- c(6e+06, 75000400, 743450000, 340000, 4300000)

вы можете сделать следующее.

paste(format(round(x / 1e6, 1), trim = TRUE), "M")
# [1] "6.0 M"   "75.0 M"  "743.5 M" "0.3 M"   "4.3 M"  

И если вас не интересуют конечные нули, просто удалите вызов format().

paste(round(x / 1e6, 1), "M")
# [1] "6 M"     "75 M"    "743.5 M" "0.3 M"   "4.3 M"  

В качестве альтернативы вы можете назначить класс S3 методом печати и сохранить y как числовое под ним. Здесь я использую paste0(), чтобы сделать результат более понятным.

print.million <- function(x, quote = FALSE, ...) {
    x <- paste0(round(x / 1e6, 1), "M")
    NextMethod(x, quote = quote, ...)
}
## assign the 'million' class to 'x'
class(x) <- "million"
x
# [1] 6M     75M    743.5M 0.3M   4.3M  
x[] 
# [1]   6000000  75000400 743450000    340000   4300000

Вы можете сделать то же самое и для миллиардов и триллионов. Для получения информации о том, как поместить это в фрейм данных, см. этот ответ, поскольку вам понадобятся как методы format(), так и as.data.frame().

Ответ 2

Это использует findInterval для определения суффикса и определения знаменателя. Может быть легко расширен в любом направлении, если вы хотите опуститься ниже 1,0 или выше 1 триллиона:

comprss <- function(tx) { 
      div <- findInterval(as.numeric(gsub("\\,", "", tx)), 
                          c(0, 1e3, 1e6, 1e9, 1e12) )
      paste(round( as.numeric(gsub("\\,","",tx))/10^(3*(div-1)), 2), 
           c("","K","M","B","T")[div] )}

Вам не нужно удалять as.numeric или gsub, если ввод числовой. Это по общему признанию излишне, но преуспело бы. Вот результат с примером Grgor:

> comprss (big_x)
 [1] "123 "     "500 "     "999 "     "1.05 K"   "9 K"     
 [6] "49 K"     "105.4 K"  "998 K"    "1.5 M"    "20 M"    
[11] "313.4 M"  "453.12 B"

И с оригинальным вводом (который был фактически переменной фактора.)

comprss (dat$V2)
[1] "6 M"      "75 M"     "743.45 M" "340 K"    "4.3 M"  

И, конечно, они могут быть напечатаны без кавычек, используя либо явную команду print и quotes = FALSE, либо используя cat.

Ответ 3

Другой вариант, начиная с числовых (а не символьных) номеров и работает как для миллионов, так и для миллиардов (и ниже). Вы можете передать больше аргументов formatC для настройки вывода и в случае необходимости перейти к Trillions.

m_b_format = function(x) {
    b.index = x >= 1e9
    m.index = x >= 1e5 & x < 1e9

    output = formatC(x, format = "d", big.mark = ",")
    output[b.index] = paste(formatC(x[b.index] / 1e9, digits = 1, format = "f"), "B")
    output[m.index] = paste(formatC(x[m.index] / 1e6, digits = 1, format = "f"), "M")
    return(output)
}

your_x = c(6e6, 75e6 + 400, 743450000, 340000, 43e6)
> m_b_format(your_x)
[1] "6.0 M"   "75.0 M"  "743.5 M" "0.3 M"   "43.0 M" 

big_x = c(123, 500, 999, 1050, 9000, 49000, 105400, 998000,
          1.5e6, 2e7, 313402182, 453123634432)
> m_b_format(big_x)
 [1] "123"     "500"     "999"    "1,050"   "9,000"    "49,000"
 [7] "0.1 M"   "1.0 M"   "1.5 M"  "20.0 M"  "313.4 M"  "453.1 B"

Ответ 4

Заимствование из других ответов и добавление к ним с основной целью создания ярких меток для осей ggplot2. И да, только положительные значения (отрицательные будут оставлены как есть), так как обычно я хочу эти суффиксы только для положительных величин. Легко распространиться на отрицательные числа.

# Format numbers with suffixes K, M, B, T and optional rounding. Vectorized
# Main purpose: pretty formatting axes for plots produced by ggplot2
#
# Usage in ggplot2: scale_x_continuous(labels = suffix_formatter)

suffix_formatter <- function(x, digits = NULL)
{
    intl <- c(1e3, 1e6, 1e9, 1e12);
    suffixes <- c('K', 'M', 'B', 'T');

    i <- findInterval(x, intl);

    result <- character(length(x));

    # Note: for ggplot2 the last label element of x is NA, so we need to handle it
    ind_format <- !is.na(x) & i > 0;

    # Format only the elements that need to be formatted 
    # with suffixes and possible rounding
    result[ind_format] <- paste0(
        formatC(x[ind_format]/intl[i[ind_format]], format = "f", digits = digits)
        ,suffixes[i[ind_format]]
    );
    # And leave the rest with no changes
    result[!ind_format] <- as.character(x[!ind_format]);

    return(invisible(result));
}

И пример использования.

x <- seq(1:10);
d <- data.frame(x = x, y = 10^x);
ggplot(aes(x=x, y=y), data = d) + geom_line() + scale_y_log10()

без форматирования суффиксов

ggplot(aes(x=x, y=y), data = d) + geom_line() + scale_y_log10(labels = suffix_formatter)

с форматированием суффиксов

Ответ 5

Я переписываю функцию @42- для размещения% чисел, вот так

compress <- function(tx) {
  tx <- as.numeric(gsub("\\,", "", tx))
  int <- c(1e-2, 1, 1e3, 1e6, 1e9, 1e12)
  div <- findInterval(tx, int)
  paste(round( tx/int[div], 2), c("%","", "K","M","B","T")[div] )
}

>tx
 total_reads  total_bases     q20_rate     q30_rate   gc_content 
3.504660e+05 1.051398e+08 6.648160e-01 4.810370e-01 5.111660e-01 
> compress(tx)
[1] "350.47 K" "105.14 M" "66.48 %"  "48.1 %"   "51.12 %" 

Это может быть полезно для аналогичной проблемы

Ответ 6

Подобно @Alex Poklonskiy, мне нужен был форматер для диаграмм. Но мне нужна была версия, которая также поддерживает отрицательные числа. Это его настраиваемая функция (хотя я не эксперт в R-программировании):

number_format <- function(x, digits = NULL)
{
  intl <- c(1e3, 1e6, 1e9, 1e12)
  suffixes <- c(' K', ' M', ' B', ' T')

  i <- findInterval(x, intl)

  i_neg <- findInterval(-x, intl)

  result <- character(length(x))

  # Note: for ggplot2 the last label element of x is NA, so we need to handle it
  ind_format <- !is.na(x) & i > 0
  neg_format <- !is.na(x) & i_neg > 0

  # Format only the elements that need to be formatted
  # with suffixes and possible rounding
  result[ind_format] <- paste0(
    formatC(x[ind_format] / intl[i[ind_format]], format = "f", digits = digits),
    suffixes[i[ind_format]]
  )
  # Format negative numbers
  result[neg_format] <- paste0(
    formatC(x[neg_format] / intl[i_neg[neg_format]], format = "f", digits = digits),
    suffixes[i_neg[neg_format]]
  )

  # To the rest only apply rounding
  result[!ind_format & !neg_format] <- as.character(
    formatC(x[!ind_format & !neg_format], format = "f", digits = digits)
  )

  return(invisible(result))
}

Я также установил, что аргумент digits используется для округления значений, которые не получают суффикс (например, 1.23434546)

Пример использования:

> print( number_format(c(1.2325353, 500, 132364584563, 5.67e+9, -2.45e+7, -1.2333, -55)) )
[1] "1.2325"     "500.0000"   "132.3646 B" "5.6700 B"   "-24.5000 M" "-1.2333"    "-55.0000"  
> print( number_format(c(1.2325353, 500, 132364584563, 5.67e+9, -2.45e+7, -1.2333, -55), digits = 2) )
[1] "1.23"     "500.00"   "132.36 B" "5.67 B"   "-24.50 M" "-1.23"    "-55.00"  

Ответ 7

dplyr casewhen теперь предлагает более дружественное решение для этого - например:

format_bignum = function(n){
  case_when(
    n >= 1e12 ~ paste(round(n/1e12), 'Tn'),
    n >= 1e9 ~ paste(round(n/1e9), 'Bn'),
    n >= 1e6 ~ paste(round(n/1e6), 'M'),
    n >= 1e3 ~ paste(round(n/1e3), 'K'),
    TRUE ~ as.character(n))
}

В качестве альтернативы вы можете встроить бит case_when в вызов mutate.