Come formattare un numero come percentuale in R?


135

Una delle cose che mi lasciava perplesso come un novizio di R era come formattare un numero come percentuale per la stampa.

Ad esempio, visualizza 0.12345come 12.345%. Ho una serie di soluzioni alternative per questo, ma nessuna di queste sembra essere "newby friendly". Per esempio:

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

Domanda: esiste una funzione R di base per farlo? In alternativa, esiste un pacchetto ampiamente utilizzato che fornisce un comodo wrapper?


Nonostante abbia cercato qualcosa del genere in ?format, ?formatCe ?prettyNum, devo ancora trovare un wrapper convenientemente conveniente nella base R. ??"percent"non ha prodotto nulla di utile. library(sos); findFn("format percent")restituisce 1250 risultati, quindi di nuovo non utile. ggplot2ha una funzione percentma questo non dà alcun controllo sulla precisione dell'arrotondamento.


5
sprintfsembra essere la soluzione preferita nelle mailing list e non ho visto nessuna soluzione migliore. Qualsiasi funzione integrata non sarà comunque molto più semplice da chiamare, giusto?
michel-slm,

1
Dal mio punto di vista sprintfva perfettamente bene per quel sottoinsieme di programmatori R che sono anche programmatori. Ho codificato molto nella mia vita, tra cui COBOL (brivido) e fortran (mostra la mia età). Ma non considero sprintfovvie le regole di formattazione (traduzione: WTF?). E ovviamente un wrapper dedicato deve essere più facile da chiamare di sprintf, ad esempio:format_percent(x=0.12345, digits=2)
Andrie

@hircus Penso che sia abbastanza comune da meritare la sua funzione al curry corto. È particolarmente un problema con Sweave, in cui \ Sexpr {sprintf (% 1.2f %% ", myvar)} è molto più brutto di \ Sexpr {pct (myvar)} o qualunque sia la funzione più breve.
Ari B. Friedman

2
Imparare a usare gli strumenti appropriati è qualcosa su cui dovremmo aspettarci gli utenti? Voglio dire, imparare a usare sprintf()non richiede molto più tempo che scoprire che il pacchetto foo contiene format_percent(). Cosa succede se l'utente non desidera formattare come percentuale ma qualcos'altro simile? Devono trovare un altro wrapper. A lungo termine l'apprendimento degli strumenti di base sarà vantaggioso.
Gavin Simpson,

1
C'è un piccolo problema in quanto %è il carattere di commento in LaTeX, che è il formato di report "predefinito" per R. Quindi, sebbene possa essere utile per etichettare i grafici, bisogna fare attenzione se il numero formattato deve essere Sweaved.
James,

Risposte:


118

Anche più tardi:

Come sottolineato da @DzimitryM, percent()è stato "ritirato" in favore di label_percent(), che è sinonimo della vecchia percent_format()funzione.

label_percent() restituisce una funzione, quindi per usarla è necessaria una coppia aggiuntiva di parentesi.

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

Personalizza questo aggiungendo argomenti all'interno della prima serie di parentesi.

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

Un aggiornamento, diversi anni dopo:

In questi giorni c'è una percentfunzione nel scalespacchetto, come documentato nella risposta di krlmlr. Usa quello invece della mia soluzione arrotolata a mano.


Prova qualcosa del genere

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

Con l'utilizzo, ad es.

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

(Se preferisci, cambia il formato da "f"a "g".)


2
Sì, questo funziona ed è una versione leggermente più generale della soluzione alternativa fornita nella domanda. Ma la mia vera domanda è se questo esiste nella base R o no.
Andrie,

Funziona per me nell'elenco delle percentuali, ma la sostituzione di "x" con "percent (x)" in un comando statistico o grafico produce un messaggio di errore.
rolando2,

@ rolando2 Sia la mia risposta che la risposta di krlmlr restituiscono vettori di caratteri come output, non numeri. Sono per la formattazione di etichette degli assi e simili. Forse vuoi solo moltiplicare per 100?
Richie Cotton,

A partire dal 2020 scalesver. 1.1.0 manuale: percent()è ritirato; si prega di utilizzare label_percent()invece, che non è adatto per la formattazione dei numeri . In modo che la soluzione lanciata a mano sia ancora pertinente
DzimitryM

74

Dai un'occhiata al scalespacchetto. Prima faceva parte ggplot2, credo.

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

La logica integrata per rilevare la precisione dovrebbe funzionare abbastanza bene nella maggior parte dei casi.

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
Non funziona con numeri negativi. percent(-0.1)produceNaN%
akhmed

1
@akhmed: questo è già stato segnalato, una correzione è disponibile ma in attesa di revisione: github.com/hadley/scales/issues/50 . Si noti che sembra funzionare per più di un numero negativo:scales::percent(c(-0.1, -0.2))
krlmlr

Grazie per il link! Non ero sicuro che si tratti di una funzionalità o di un bug. Per più numeri a volte funziona e talvolta no. Dì, scales::percent(c(-0.1,-0.1,-0.1))produce "NaN%" "NaN%" "NaN%"ma il tuo esempio funziona. Per il riferimento di altri, il bug non è stato ancora risolto al momento scales_0.2.4. Inoltre, a partire da oggi, la corrispondente richiesta pull che la corregge non è ancora unita al ramo principale.
akhmed,

34

Scopri la percentfunzione dal formattablepacchetto:

library(formattable)
x <- c(0.23, 0.95, 0.3)
percent(x)
[1] 23.00% 95.00% 30.00%

4
+1, questo consente di specificare quante cifre includere, cosa che scales::percentnelle prime due risposte no.
Sam Firke,

3
+1, anche se è abbastanza facile eseguire la tua funzione, è molto utile scegliere il numero di cifre.
Gang Su

10

Ho fatto un benchmarking per la velocità su queste risposte e sono stato sorpreso di vedere percentnel scalespacchetto così pubblicizzato, data la sua lentezza. Immagino che il vantaggio sia il suo rilevatore automatico per una corretta formattazione, ma se sai che aspetto hanno i tuoi dati, sembra chiaro che devono essere evitati.

Ecco i risultati del tentativo di formattare un elenco di 100.000 percentuali in (0,1) a una percentuale in 2 cifre:

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

Quindi sprintfemerge come un chiaro vincitore quando vogliamo aggiungere un segno di percentuale. D'altra parte, se vogliamo solo moltiplicare il numero e arrotondare (passare dalla proporzione alla percentuale senza "%", allora round()è il più veloce:

# 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

È possibile utilizzare il pacchetto scale solo per questa operazione (senza caricarlo con request o library)

scales::percent(m)

1
Come dare la precisione per il numero di cifre?
Elmex80s

6

Ecco la mia soluzione per definire una nuova funzione (principalmente così posso giocare con Curry e Compose :-)):

library(roxygen)
printpct <- Compose(function(x) x*100, Curry(sprintf,fmt="%1.2f%%"))

3

Vedendo come scalable::percentera già stato dimostrato che era più lento e Liliana Pacheco offriva un'altra soluzione, sono andato avanti e ho provato a confrontarlo con alcune delle altre opzioni basate sull'esempio di Michael:

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

Questi sono i risultati che ho ottenuto:

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

Non ho idea, però, del perché krlmlr()e delle mie andrie1()esibizioni sia andata molto peggio che nell'esempio di MichaelChirico. Qualche indizio?


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

Questa funzione potrebbe trasformare i dati in percentuali per colonne

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

L'aritmetica di base è vettorializzata --- il ciclo interno per è inefficiente e non necessario. Può essere sostituito con base2[, j] = base[ , j] * 100 / suma.c. Vale anche la pena notare che questa non è esattamente una risposta alla domanda ... la domanda riguarda la formattazione del 0.5"50,0%", non il calcolo ...
Gregor Thomas

0

La tidyverseversione è questa:

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

Sembra ordinato come al solito.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.