Как отформатировать число в процентах в R?

135

Одна из вещей, которая раньше смущала меня как новичка в R, заключалась в том, как отформатировать число в процентах для печати.

Например, отобразить 0.12345как 12.345%. У меня есть несколько обходных путей для этого, но ни один из них не кажется "дружелюбным для новичков". Например:

set.seed(1)
m <- runif(5)

paste(round(100*m, 2), "%", sep="")
[1] "26.55%" "37.21%" "57.29%" "90.82%" "20.17%"

sprintf("%1.2f%%", 100*m)
[1] "26.55%" "37.21%" "57.29%" "90.82%" "20.17%"

Вопрос: есть ли для этого базовая функция R? В качестве альтернативы, существует ли широко используемый пакет, обеспечивающий удобную оболочку?


Несмотря на поиски чего - то , как это в ?format, ?formatCи ?prettyNumя до сих пор найти подходяще удобную оболочку в базе R. ??"percent"ничего полезного не даст. library(sos); findFn("format percent")возвращает 1250 совпадений - снова бесполезно. ggplot2есть функция, percentно она не позволяет контролировать точность округления.

Andrie
источник
5
sprintfкажется, это любимое решение в списках рассылки, и я не видел лучшего решения. Все равно вызвать встроенную функцию будет не так просто, верно?
michel-slm
1
На мой взгляд, sprintfэто прекрасно подходит для той части R-кодеров, которые тоже являются программистами. Я много кодировал в своей жизни, в том числе COBOL (вздрагивает) и fortran (показывает мой возраст). Но я не считаю sprintfправила форматирования очевидными (перевод: WTF?). И, конечно же, специальную оболочку должно быть проще вызвать, чем sprintf, например:format_percent(x=0.12345, digits=2)
Andrie
@hircus Я думаю, что это достаточно распространено, поэтому оно заслуживает собственной короткой функции карри. Это особенно проблема Sweave, где \ Sexpr {sprintf (% 1.2f %% ", myvar)} намного уродливее, чем \ Sexpr {pct (myvar)} или какой-либо другой более короткой функции.
Ари Б. Фридман,
2
Разве мы не должны ожидать, что пользователи будут стремиться к обучению использованию соответствующих инструментов? Я имею в виду, что обучение использованию sprintf()вряд ли займет больше времени, чем выяснение того, что пакет foo содержит format_percent(). Что произойдет, если пользователь хочет форматировать не в процентах, а в чем-то похожем? Им нужно найти другую обертку. В конечном итоге изучение базовых инструментов принесет пользу.
Гэвин Симпсон
1
Есть небольшая проблема в том %, что символ комментария в LaTeX, который является форматом отчетности "по умолчанию" для R. Таким образом, хотя он может быть полезен для маркировки графиков, необходимо соблюдать осторожность, если форматируемое число должно быть свернуто.
Джеймс

Ответы:

118

Даже позже:

Как указывает @DzimitryM, percent()был «удален» в пользу label_percent(), что является синонимом старой percent_format()функции.

label_percent() возвращает функцию, поэтому для ее использования вам понадобится пара дополнительных скобок.

library(scales)
x <- c(-1, 0, 0.1, 0.555555, 1, 100)
label_percent()(x)
## [1] "-100%"   "0%"      "10%"     "56%"     "100%"    "10 000%"

Настройте это, добавив аргументы внутри первого набора круглых скобок.

label_percent(big.mark = ",", suffix = " percent")(x)
## [1] "-100 percent"   "0 percent"      "10 percent"    
## [4] "56 percent"     "100 percent"    "10,000 percent"

Обновление, несколько лет спустя:

В наши дни percentв scalesпакете есть функция , как описано в ответе krlmlr. Используйте это вместо моего решения, скрученного вручную.


Попробуйте что-то вроде

percent <- function(x, digits = 2, format = "f", ...) {
  paste0(formatC(100 * x, format = format, digits = digits, ...), "%")
}

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

x <- c(-1, 0, 0.1, 0.555555, 1, 100)
percent(x)

(Если хотите, измените формат с "f"на "g".)

Ричи Коттон
источник
2
Да, это работает и является немного более общей версией обходного пути, который я указал в вопросе. Но мой настоящий вопрос заключается в том, существует ли это в базе R или нет.
Андри
У меня работает при перечислении процентов, но замена «x» на «проценты (x)» в статистической или графической команде вызывает сообщение об ошибке.
rolando2
@ rolando2 И мой ответ, и ответ krlmlr возвращают в качестве выходных данных векторы символов, а не числа. Они предназначены для форматирования меток осей и т.п. Возможно, вы просто хотите умножить на 100?
Richie Cotton
По состоянию на 2020 scalesвер. Руководство 1.1.0 сообщает: percent()удалено; используйте label_percent()вместо этого, что не подходит для форматирования чисел . Так что ручное решение еще актуально
DzimitryM 06
74

Ознакомьтесь с scalesпакетом. ggplot2Думаю, раньше это было частью .

library('scales')
percent((1:10) / 100)
#  [1] "1%"  "2%"  "3%"  "4%"  "5%"  "6%"  "7%"  "8%"  "9%"  "10%"

Встроенная логика для определения точности должна работать достаточно хорошо для большинства случаев.

percent((1:10) / 1000)
#  [1] "0.1%" "0.2%" "0.3%" "0.4%" "0.5%" "0.6%" "0.7%" "0.8%" "0.9%" "1.0%"
percent((1:10) / 100000)
#  [1] "0.001%" "0.002%" "0.003%" "0.004%" "0.005%" "0.006%" "0.007%" "0.008%"
#  [9] "0.009%" "0.010%"
percent(sqrt(seq(0, 1, by=0.1)))
#  [1] "0%"   "32%"  "45%"  "55%"  "63%"  "71%"  "77%"  "84%"  "89%"  "95%" 
# [11] "100%"
percent(seq(0, 0.1, by=0.01) ** 2)
#  [1] "0.00%" "0.01%" "0.04%" "0.09%" "0.16%" "0.25%" "0.36%" "0.49%" "0.64%"
# [10] "0.81%" "1.00%"
krlmlr
источник
2
Не работает для отрицательных чисел. percent(-0.1)производитNaN%
Ахмед
1
@akhmed: Об этом уже сообщалось, исправление доступно, но ожидает рассмотрения: github.com/hadley/scales/issues/50 . Обратите внимание, что, похоже, это работает для более чем одного отрицательного числа:scales::percent(c(-0.1, -0.2))
krlmlr
Спасибо за ссылку! Я не был уверен, ошибка это или функция. Для нескольких номеров это иногда работает, а иногда нет. Скажем, scales::percent(c(-0.1,-0.1,-0.1))производит, "NaN%" "NaN%" "NaN%"но ваш пример работает. Для справки, ошибка еще не исправлена scales_0.2.4. Кроме того, на сегодняшний день соответствующее исправление запроса на перенос еще не объединено с основной веткой.
Ахмед
34

Ознакомьтесь с percentфункцией из formattableпакета:

library(formattable)
x <- c(0.23, 0.95, 0.3)
percent(x)
[1] 23.00% 95.00% 30.00%
Лилиана Пачеко
источник
4
+1, это позволяет указать, сколько цифр включать, чего scales::percentв первых двух ответах нет.
Сэм Фирке
3
+1, несмотря на то, что свернуть свою собственную функцию довольно просто, выбор количества цифр действительно полезен.
Ган Су
10

Я сделал некоторые бенчмаркинг скорости на этих ответов и был удивлен увидеть percentв scalesпакете так расхваливали, учитывая его инерционность. Я полагаю, что преимуществом является его автоматический детектор для правильного форматирования, но если вы знаете, как выглядят ваши данные, кажется, что этого следует избегать.

Вот результаты попытки отформатировать список из 100000 процентов в (0,1) до процента в 2 цифры:

library(microbenchmark)
x = runif(1e5)
microbenchmark(times = 100L, andrie1(), andrie2(), richie(), krlmlr())
# Unit: milliseconds
#   expr       min        lq      mean    median        uq       max
# 1 andrie1()  91.08811  95.51952  99.54368  97.39548 102.75665 126.54918 #paste(round())
# 2 andrie2()  43.75678  45.56284  49.20919  47.42042  51.23483  69.10444 #sprintf()
# 3  richie()  79.35606  82.30379  87.29905  84.47743  90.38425 112.22889 #paste(formatC())
# 4  krlmlr() 243.19699 267.74435 304.16202 280.28878 311.41978 534.55904 #scales::percent()

Таким образом sprintfстановится очевидным победителем, когда мы хотим добавить знак процента. С другой стороны, если мы хотим только умножить число и округлить (перейти от пропорции к проценту без "%", то round()будет быстрее:

# Unit: milliseconds
#        expr      min        lq      mean    median        uq       max
# 1 andrie1()  4.43576  4.514349  4.583014  4.547911  4.640199  4.939159 # round()
# 2 andrie2() 42.26545 42.462963 43.229595 42.960719 43.642912 47.344517 # sprintf()
# 3  richie() 64.99420 65.872592 67.480730 66.731730 67.950658 96.722691 # formatC()
MichaelChirico
источник
8

Вы можете использовать пакет scale только для этой операции (не загружая его с помощью require или библиотеки)

scales::percent(m)
בנימן הגלילי
источник
1
Как дать точность количества цифр?
Elmex80s
6

Вот мое решение для определения новой функции (в основном, чтобы я мог поиграть с Curry и Compose :-)):

library(roxygen)
printpct <- Compose(function(x) x*100, Curry(sprintf,fmt="%1.2f%%"))
Ари Б. Фридман
источник
3

Увидев, как scalable::percentуже было показано, что он самый медленный, и Лилиана Пачеко предлагает другое решение, я пошел дальше и попытался сравнить его с некоторыми другими вариантами на основе примера, приведенного Майклом:

library(microbenchmark)
library(scales)
library(formattable)

x<-runif(1e5)

lilip <- function() formattable::percent(x,2)
krlmlr <- function() scales::percent(x)
andrie1 <- function() paste0(round(x,4) * 100, '%')

microbenchmark(times=100L,lilip(), krlmlr(), andrie1())

Вот результаты, которые я получил:

Unit: microseconds
      expr        min          lq        mean      median          uq        max neval
   lilip()    194.562    373.7335    772.5663    889.7045    950.4035   1611.537   100
  krlmlr() 226270.845 237985.6560 260194.9269 251581.0235 280704.2320 373022.180   100
 andrie1()  87916.021  90437.4820  92791.8923  92636.8420  94448.7040 102543.252   100

Я понятия не имею, почему мой krlmlr()и andrie1()выступил намного хуже, чем в примере с Михаэлем Кирико. Есть какие-нибудь подсказки?

matt_jay
источник
0
try this~

data_format <- function(data,digit=2,type='%'){
if(type=='d') {
    type = 'f';
    digit = 0;
}
switch(type,
    '%' = {format <- paste("%.", digit, "f%", type, sep='');num <- 100},
    'f' = {format <- paste("%.", digit, type, sep='');num <- 1},
    cat(type, "is not a recognized type\n")
)
sprintf(format, num * data)
}
свет голос
источник
0

Эта функция может преобразовывать данные в проценты по столбцам

percent.colmns = function(base, columnas = 1:ncol(base), filas = 1:nrow(base)){
    base2 = base
    for(j in columnas){
        suma.c = sum(base[,j])
        for(i in filas){
            base2[i,j] = base[i,j]*100/suma.c
        }
    }
    return(base2)
}
Эдвин Торрес
источник
Базовая арифметика векторизована - внутренний цикл for неэффективен и ненужен. Можно заменить на base2[, j] = base[ , j] * 100 / suma.c. Также стоит отметить, что это не совсем ответ на вопрос ... вопрос заключается в форматировании чего-то вроде 0.5"50,0%", а не в вычислении ...
Грегор Томас,
0

tidyverseВерсия такова:

> library(tidyverse)

> set.seed(1)
> m <- runif(5)
> dt <- as.data.frame(m)

> dt %>% mutate(perc=scales::percent(m,accuracy=0.001))
          m    perc
1 0.2655087 26.551%
2 0.3721239 37.212%
3 0.5728534 57.285%
4 0.9082078 90.821%
5 0.2016819 20.168%

Выглядит как всегда аккуратно.

Giacomo
источник