Як відформатувати число у відсотках у 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але це не дає контролю над точністю округлення.


5
sprintfздається, улюблене рішення у списках розсилки, і я не бачив кращого рішення. Будь-яка вбудована функція все одно не буде набагато простішою, так?
michel-slm

1
На мій погляд, sprintfце цілком чудово для тієї підмножини кодерів R, які також є програмістами. Я багато кодував у своєму житті, включаючи COBOL (здригання) та fortran (показує свій вік). Але я не вважаю sprintfправила форматування очевидними (переклад: WTF?). І звичайно, спеціалізовану обгортку потрібно простіше зателефонувати, ніж спринт, наприклад:format_percent(x=0.12345, digits=2)
Андрі

@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?
Річі Коттон

Станом на 2020 рік scales. 1.1.0 посібник повідомляє: percent()пенсіонер; будь ласка, використовуйте label_percent()замість цього, що не підходить для форматування чисел . Так що ручний прокат все ще актуальний
DzimitryM

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%"

2
Не працює для від'ємних чисел. percent(-0.1)продукуєNaN%
ахмед

1
@akhmed: Про це вже повідомлялося, виправлення доступне, але очікує на розгляд: github.com/hadley/scales/isissue/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пакеті так розрекламований, враховуючи його млявість. Я думаю, що перевагою є його автоматичний детектор для правильного форматування, але якщо ви знаєте, як виглядають ваші дані, здається, цього уникати.

Ось результати спроби відформатувати список від 100 000 відсотків у (0,1) до відсотка у двох цифрах:

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()

8

Ви можете використовувати пакет масштабів саме для цієї операції (не завантажуючи його з вимогою чи бібліотекою)

scales::percent(m)

1
Як надати точність кількості цифр?
Elmex80s

6

Ось моє рішення щодо визначення нової функції (здебільшого я можу пограти з Curry and 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()діяло так гірше, ніж на прикладі МайклаЧіріко. Будь-які підказки?


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)
}

Основна арифметика векторизована --- внутрішня для циклу неефективна і непотрібна. Можна замінити на 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%

Виглядає охайно, як завжди.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.