¿Cómo formatear un número como porcentaje en R?

135

Una de las cosas que solía dejarme perplejo como novato en R era cómo formatear un número como porcentaje para imprimir.

Por ejemplo, mostrar 0.12345como 12.345%. Tengo varias soluciones para esto, pero ninguna de estas parece ser "nueva para los amigos". Por ejemplo:

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

Pregunta: ¿Hay una función R base para hacer esto? Alternativamente, ¿hay un paquete ampliamente utilizado que proporcione una envoltura conveniente?


A pesar de buscar algo como esto en ?format, ?formatCy ?prettyNum, todavía tengo que encontrar una envoltura convenientemente conveniente en la base R. ??"percent"no produjo nada útil. library(sos); findFn("format percent")devuelve 1250 visitas, así que de nuevo no es útil. ggplot2tiene una función percentpero esto no da control sobre la precisión de redondeo.

Andrie
fuente
55
sprintfParece ser la solución favorita en las listas de correo, y no he visto ninguna solución mejor. Cualquier función incorporada no será mucho más fácil de llamar de todos modos, ¿verdad?
michel-slm
1
En mi opinión, sprintfestá perfectamente bien para ese subconjunto de codificadores R que también resultan ser programadores. He codificado mucho en mi vida, incluyendo COBOL (estremecimiento) y fortran (muestra mi edad). Pero no considero sprintfobvias las reglas de formato (traducción: ¿WTF?). Y, por supuesto, un contenedor dedicado debe ser más fácil de llamar que sprintf, por ejemplo:format_percent(x=0.12345, digits=2)
Andrie
@hircus Creo que es lo suficientemente común como para que merezca su propia función de curry corto. Es particularmente un problema con Sweave, donde \ Sexpr {sprintf (% 1.2f %% ", myvar)} es mucho más feo que \ Sexpr {pct (myvar)} o cualquiera que sea la función más corta.
Ari B. Friedman
2
¿No es aprender a usar las herramientas apropiadas algo por lo que debemos esperar que los usuarios se esfuercen? Es decir, aprender a usar sprintf()es apenas más tiempo que la de descubrir que el paquete foo contiene format_percent(). ¿Qué sucede si el usuario no quiere formatear como porcentaje, sino algo similar? Necesitan encontrar otro contenedor. A la larga, aprender las herramientas básicas será beneficioso.
Gavin Simpson
1
Hay un pequeño problema en %el carácter de comentario en LaTeX, que es el formato de informe "predeterminado" para R. Por lo tanto, si bien puede ser útil para etiquetar gráficos, se debe tener cuidado si el número formateado debe ser Sweaved.
James

Respuestas:

118

Aun mas tarde:

Como señaló @DzimitryM, percent()ha sido "retirado" a favor de label_percent(), que es sinónimo de la percent_format()función anterior.

label_percent() devuelve una función, por lo que para usarla, necesita un par adicional de paréntesis.

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

Personalice esto agregando argumentos dentro del primer conjunto de paréntesis.

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

Una actualización, varios años después:

En estos días hay una percentfunción en el scalespaquete, como se documenta en la respuesta de krlmlr. Use eso en lugar de mi solución enrollada a mano.


Intenta algo como

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

Con el uso, por ejemplo,

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

(Si lo prefiere, cambie el formato de "f"a "g").

Algodón Richie
fuente
2
Sí, esto funciona, y es una versión un poco más general de la solución que proporcioné en la pregunta. Pero mi verdadera pregunta es si esto existe en la base R o no.
Andrie
Funciona para mí al enumerar porcentajes, pero reemplazar "x" con "porcentaje (x)" en un comando estadístico o gráfico produce un mensaje de error.
rolando2
@ rolando2 Tanto mi respuesta como la respuesta de krlmlr devuelven vectores de caracteres como salida, no números. Son para formatear etiquetas de eje y similares. ¿Quizás solo quieres multiplicar por 100?
Richie Cotton
A partir de 2020 scalesver. 1.1.0 manual dice: percent()está retirado; utilice label_percent()en su lugar, lo que no es adecuado para el formato de números . Para que la solución enrollada a mano siga siendo relevante
DzimitryM
74

Mira el scalespaquete. Solía ​​ser parte de ggplot2, creo.

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

La lógica incorporada para detectar la precisión debería funcionar lo suficientemente bien para la mayoría de los casos.

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
fuente
2
No funciona para números negativos. percent(-0.1)produceNaN%
akhmed
1
@akhmed: Esto ya se informó, hay una solución disponible pero pendiente de revisión: github.com/hadley/scales/issues/50 . Tenga en cuenta que parece funcionar para más de un número negativo:scales::percent(c(-0.1, -0.2))
krlmlr
Gracias por el enlace! No estaba seguro de si es una característica o un error. Para números múltiples a veces funciona y a veces no. Digamos, scales::percent(c(-0.1,-0.1,-0.1))produce "NaN%" "NaN%" "NaN%"pero tu ejemplo funciona. Para referencia de otros, el error aún no se ha solucionado a partir de scales_0.2.4. Además, a partir de hoy, la solicitud de extracción correspondiente que lo arregla aún no se ha fusionado en la rama principal.
akhmed
34

Echa un vistazo a la percentfunción del formattablepaquete:

library(formattable)
x <- c(0.23, 0.95, 0.3)
percent(x)
[1] 23.00% 95.00% 30.00%
Liliana Pacheco
fuente
44
+1, esto permite especificar cuántos dígitos incluir, lo que scales::percenten las dos primeras respuestas no.
Sam Firke
3
+1, aunque es bastante fácil rodar su propia función, permitir elegir el número de dígitos es realmente útil.
Gang Su
10

Hice algunas evaluaciones comparativas para la velocidad en estas respuestas y me sorprendió ver percenten el scalespaquete tan promocionado, dada su lentitud. Me imagino que la ventaja es su detector automático para un formateo adecuado, pero si sabe cómo se ven sus datos, parece claro que debe evitarse.

Estos son los resultados de intentar formatear una lista de 100,000 porcentajes en (0,1) a un porcentaje en 2 dígitos:

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

Entonces sprintfsurge como un claro ganador cuando queremos agregar un signo de porcentaje. Por otro lado, si solo queremos multiplicar el número y redondear (pasar de proporción a porcentaje sin "%", entonces round()es más rápido:

# 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
fuente
8

Puede usar el paquete de escalas solo para esta operación (sin cargarlo con require o library)

scales::percent(m)
בנימן הגלילי
fuente
1
¿Cómo dar la precisión para el número de dígitos?
Elmex80s
6

Aquí está mi solución para definir una nueva función (principalmente para poder jugar con Curry y Compose :-)):

library(roxygen)
printpct <- Compose(function(x) x*100, Curry(sprintf,fmt="%1.2f%%"))
Ari B. Friedman
fuente
3

Al ver cómo scalable::percentya se había demostrado que era más lento y Liliana Pacheco ofrecía otra solución, seguí adelante e intenté compararlo con algunas de las otras opciones basadas en el ejemplo que Michael estableció:

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

Estos son los resultados que obtuve:

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

No tengo ni idea, sin embargo, por qué mi krlmlr()y andrie1()realiza de manera mucho peor que en el ejemplo de MichaelChirico. ¿Alguna pista?

matt_jay
fuente
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)
}
voz ligera
fuente
0

Esta función podría transformar los datos en porcentajes por columnas.

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)
}
Edwin Torres
fuente
La aritmética básica está vectorizada: el bucle for interno es ineficiente e innecesario. Se puede reemplazar con base2[, j] = base[ , j] * 100 / suma.c. También vale la pena señalar que esto no es exactamente una respuesta a la pregunta ... la pregunta es sobre formatear algo como 0.5"50.0%", no sobre hacer un cálculo ...
Gregor Thomas
0

La tidyverseversión es esta:

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

Se ve ordenado como de costumbre.

Giacomo
fuente