Wie formatiere ich eine Zahl als Prozentsatz in R?

134

Eines der Dinge, die mich als Neuling bei R verwirrten, war das Formatieren einer Zahl als Prozentsatz für das Drucken.

Zum Beispiel anzeigen 0.12345als 12.345%. Ich habe eine Reihe von Problemumgehungen dafür, aber keine davon scheint "neuartig freundlich" zu sein. Beispielsweise:

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

Frage: Gibt es dafür eine Basis-R-Funktion? Gibt es alternativ ein weit verbreitetes Paket, das eine praktische Verpackung bietet?


Trotz der Suche nach so etwas wie dieser in ?format, ?formatCund ?prettyNumhabe ich noch eine entsprechend bequeme Wrapper in der Basis zu finden R. ??"percent"nichts brachte nützlich. library(sos); findFn("format percent")gibt 1250 Treffer zurück - also wieder nicht sinnvoll. ggplot2hat eine Funktion, percentaber dies gibt keine Kontrolle über die Rundungsgenauigkeit.

Andrie
quelle
5
sprintfscheint die Lieblingslösung auf den Mailinglisten zu sein, und ich habe keine bessere Lösung gesehen. Eine eingebaute Funktion ist sowieso nicht viel einfacher aufzurufen, oder?
Michel-Slm
1
Meiner Ansicht nach sprintfist dies vollkommen in Ordnung für diese Untergruppe von R-Codierern, die zufällig auch Programmierer sind. Ich habe in meinem Leben viel programmiert, einschließlich COBOL (Schauder) und Fortran (zeigt mein Alter). Die sprintfFormatierungsregeln halte ich jedoch nicht für offensichtlich (Übersetzung: WTF?). Und natürlich muss ein dedizierter Wrapper einfacher aufzurufen sein als sprintf, zum Beispiel:format_percent(x=0.12345, digits=2)
Andrie
@hircus Ich denke, es ist häufig genug, dass es seine eigene Funktion mit kurzen Currys verdient. Es ist besonders ein Problem mit Sweave, wo \ Sexpr {sprintf (% 1.2f %% ", myvar)} viel hässlicher ist als \ Sexpr {pct (myvar)} oder was auch immer die kürzere Funktion sein würde.
Ari B. Friedman
2
Ist es nicht etwas, von dem wir erwarten sollten, dass Benutzer lernen, die entsprechenden Tools zu verwenden? Ich meine, das Erlernen des Gebrauchs sprintf()ist kaum zeitaufwändiger als herauszufinden, dass das Paket foo enthält format_percent(). Was passiert, wenn der Benutzer dann nicht in Prozent formatieren möchte, sondern etwas anderes, das ähnlich ist? Sie müssen einen anderen Wrapper finden. Auf lange Sicht werden die Basiswerkzeuge von Vorteil sein.
Gavin Simpson
1
Es gibt ein kleines Problem darin, dass %das Kommentarzeichen in LaTeX das "Standard" -Berichtsformat für R ist. Obwohl es für die Beschriftung von Diagrammen nützlich sein kann, muss vorsichtig vorgegangen werden, wenn die formatierte Zahl umgeschichtet werden soll.
James

Antworten:

118

Noch später:

Wie von @DzimitryM hervorgehoben, percent()wurde zugunsten von "zurückgezogen" label_percent(), was ein Synonym für die alte percent_format()Funktion ist.

label_percent() Gibt eine Funktion zurück. Um sie zu verwenden, benötigen Sie ein zusätzliches Klammerpaar.

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

Passen Sie dies an, indem Sie Argumente in den ersten Klammern einfügen.

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

Ein Update, einige Jahre später:

Heutzutage enthält percentdas scalesPaket eine Funktion , wie in der Antwort von krlmlr dokumentiert. Verwenden Sie das anstelle meiner handgerollten Lösung.


Versuchen Sie etwas wie

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

Bei Verwendung z.

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

(Wenn Sie möchten, ändern Sie das Format von "f"auf "g".)

Richie Cotton
quelle
2
Ja, dies funktioniert und ist eine etwas allgemeinere Version der Problemumgehung, die ich in der Frage angegeben habe. Aber meine eigentliche Frage ist, ob dies in Basis R existiert oder nicht.
Andrie
Funktioniert für mich bei der Auflistung Prozenten, aber das Ersetzen von "x" durch "Prozent (x)" in einem statistischen oder grafischen Befehl erzeugt eine Fehlermeldung.
Rolando2
@ rolando2 Sowohl meine Antwort als auch die Antwort von krlmlr geben Zeichenvektoren als Ausgabe zurück, keine Zahlen. Sie dienen zum Formatieren von Achsenbeschriftungen und dergleichen. Vielleicht möchten Sie nur mit 100 multiplizieren?
Richie Cotton
Ab 2020 scalesver. 1.1.0 Handbuch sagt: percent()ist im Ruhestand; Bitte verwenden Sie label_percent()stattdessen, was nicht für die Formatierung von Zahlen geeignet ist . Damit die handgerollte Lösung noch relevant ist
DzimitryM
74

Überprüfen Sie das scalesPaket. Es war ein Teil von ggplot2, denke ich.

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

Die integrierte Logik zum Erkennen der Genauigkeit sollte in den meisten Fällen gut genug funktionieren.

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
quelle
2
Funktioniert nicht für negative Zahlen. percent(-0.1)produziertNaN%
akhmed
1
@akhmed: Dies wurde bereits gemeldet, ein Fix ist verfügbar, die Überprüfung steht jedoch noch aus: github.com/hadley/scales/issues/50 . Beachten Sie, dass es für mehr als eine negative Zahl zu funktionieren scheint:scales::percent(c(-0.1, -0.2))
krlmlr
Danke für den Link! Ich war mir nicht sicher, ob es sich um eine Funktion oder einen Fehler handelt. Bei mehreren Nummern funktioniert es manchmal und manchmal nicht. Sagen wir, scales::percent(c(-0.1,-0.1,-0.1))produziert, "NaN%" "NaN%" "NaN%"aber Ihr Beispiel funktioniert. Als Referenz für andere ist der Fehler noch nicht behoben scales_0.2.4. Außerdem ist die entsprechende Pull-Request-Korrektur, die sie behebt, bis heute noch nicht in der Hauptniederlassung zusammengeführt.
Akhmed
34

Überprüfen Sie die percentFunktion aus dem formattablePaket:

library(formattable)
x <- c(0.23, 0.95, 0.3)
percent(x)
[1] 23.00% 95.00% 30.00%
Liliana Pacheco
quelle
4
+1 ermöglicht dies die Angabe, wie viele Ziffern eingeschlossen werden sollen, was scales::percentin den ersten beiden Antworten nicht der Fall ist.
Sam Firke
3
+1, obwohl es ziemlich einfach ist, eine eigene Funktion zu erstellen, ist es sehr nützlich, die Anzahl der Stellen zu wählen.
Gang Su
10

Ich habe einige Benchmarking-Tests für die Geschwindigkeit dieser Antworten durchgeführt und war überrascht, dass percentdas scalesso angepriesene Paket angesichts seiner Trägheit in diesem Paket angepriesen wurde. Ich stelle mir vor, der Vorteil ist der automatische Detektor für die richtige Formatierung, aber wenn Sie wissen, wie Ihre Daten aussehen, scheint dies eindeutig vermieden zu werden.

Hier sind die Ergebnisse des Versuchs, eine Liste mit 100.000 Prozent in (0,1) auf einen Prozentsatz in zwei Ziffern zu formatieren:

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

So sprintfentsteht ein klarer Gewinner, wenn wir ein Prozentzeichen hinzufügen wollen. Wenn wir dagegen nur die Zahl und die Runde multiplizieren möchten (von Proportionen zu Prozent ohne "%" wechseln), round()ist dies am schnellsten:

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

Sie können das Waagenpaket nur für diesen Vorgang verwenden (ohne es mit require oder library zu laden).

scales::percent(m)
בנימן הגלילי
quelle
1
Wie kann man die Genauigkeit für die Anzahl der Ziffern angeben?
Elmex80s
6

Hier ist meine Lösung zum Definieren einer neuen Funktion (hauptsächlich, damit ich mit Curry herumspielen und komponieren kann :-)):

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

Als scalable::percentich sah, dass es sich bereits als am langsamsten erwiesen hatte und Liliana Pacheco eine andere Lösung anbot, versuchte ich, sie anhand des Beispiels von Michael mit einigen anderen Optionen zu vergleichen:

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

Dies sind die Ergebnisse, die ich erhalten habe:

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

Ich habe jedoch keine Ahnung, warum meine krlmlr()und andrie1()so viel schlechter abschnitten als in MichaelChiricos Beispiel. Irgendwelche Hinweise?

matt_jay
quelle
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)
}
leichte Stimme
quelle
0

Diese Funktion könnte die Daten in Prozentsätze nach Spalten umwandeln

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
quelle
Grundlegende Arithmetik wird vektorisiert - die innere for-Schleife ist ineffizient und unnötig. Kann durch ersetzt werden base2[, j] = base[ , j] * 100 / suma.c. Es ist auch erwähnenswert, dass dies nicht gerade eine Antwort auf die Frage ist ... bei der Frage geht es darum, etwas wie 0.5"50,0%" zu formatieren , nicht um eine Berechnung
Gregor Thomas
0

Die tidyverseVersion ist folgende:

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

Sieht ordentlich aus wie immer.

Giacomo
quelle