Jak sformatować liczbę jako procent w R?


142

Jedną z rzeczy, które wprawiały mnie w zakłopotanie jako nowicjusza w R, było formatowanie liczby jako wartości procentowej do drukowania.

Na przykład wyświetl 0.12345jako 12.345%. Mam kilka obejść tego problemu, ale żadne z nich nie wydaje się być „przyjazne dla początkujących”. Na przykład:

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

Pytanie: Czy jest do tego podstawowa funkcja R? Alternatywnie, czy istnieje powszechnie używane opakowanie, które zapewnia wygodne opakowanie?


Pomimo szukając czegoś takiego w ?format, ?formatCi ?prettyNummuszę jeszcze znaleźć odpowiednio wygodne opakowanie w bazie R. ??"percent"niczego nie przydatnych wydajnością. library(sos); findFn("format percent")zwraca 1250 trafień - więc znowu nie przydatne. ggplot2ma funkcję, percentale nie daje kontroli nad dokładnością zaokrąglania.


5
sprintfwydaje się być ulubionym rozwiązaniem na listach mailingowych i nie widziałem lepszego rozwiązania. Każda wbudowana funkcja i tak nie będzie o wiele prostsza do wywołania, prawda?
michel-slm

1
Moim zdaniem sprintfjest to całkowicie w porządku dla podzbioru koderów R, którzy są również programistami. Wiele w życiu kodowałem, w tym COBOL (dreszcz) i fortran (pokazuje mój wiek). Ale nie uważam sprintfreguł formatowania za oczywiste (tłumaczenie: WTF?). I oczywiście dedykowany wrapper musi być łatwiejszy do wywołania niż sprintf, na przykład:format_percent(x=0.12345, digits=2)
Andrie

@hircus Myślę, że jest to na tyle powszechne, że zasługuje na własną funkcję krótkiego curry. Jest to szczególnie problem w Sweave, gdzie \ Sexpr {sprintf (% 1.2f %% ", myvar)} jest dużo brzydszy niż \ Sexpr {pct (myvar)} lub jakakolwiek krótsza funkcja byłaby.
Ari B. Friedman,

2
Czy nauka korzystania z odpowiednich narzędzi nie jest czymś, do czego powinniśmy dążyć od użytkowników? Chodzi mi o to, że nauka używania sprintf()nie zajmuje więcej czasu niż odkrycie, że pakiet foo zawiera format_percent(). Co się stanie, jeśli użytkownik nie chce wtedy formatować jako procent, ale coś innego, co jest podobne? Muszą znaleźć inne opakowanie. W dłuższej perspektywie nauka podstawowych narzędzi będzie korzystna.
Gavin Simpson

1
Występuje niewielki problem w tym, %że znak komentarza w LaTeX jest „domyślnym” formatem raportowania dla R. Więc chociaż może to być przydatne do etykietowania wykresów, należy zachować ostrożność, jeśli sformatowana liczba ma zostać przeszukana.
James

Odpowiedzi:


121

Nawet później:

Jak wskazał @DzimitryM, percent()został „wycofany” na rzecz label_percent(), co jest synonimem starej percent_format()funkcji.

label_percent() zwraca funkcję, więc aby jej użyć, potrzebujesz dodatkowej pary nawiasów.

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

Dostosuj to, dodając argumenty w pierwszym zestawie nawiasów.

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

Aktualizacja, kilka lat później:

W dzisiejszych czasach nie ma percentfunkcji w scalespakiecie, co zostało udokumentowane w odpowiedzi krlmlr użytkownika. Użyj tego zamiast mojego ręcznie zwijanego rozwiązania.


Spróbuj czegoś takiego

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

Przy użyciu np.

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

(Jeśli wolisz, zmień format z "f"na "g".)


2
Tak, to działa i jest nieco bardziej ogólną wersją obejścia, które podałem w pytaniu. Ale moje prawdziwe pytanie brzmi, czy to istnieje w bazie R, czy nie.
Andrie

Pracuje dla mnie w wykazie procentach, ale zastępując „X” z „procent (X)” w poleceniu lub wykresów statystycznych wywołuje komunikat o błędzie.
rolando2

@ rolando2 Zarówno moja odpowiedź, jak i odpowiedź krlmlr zwracają wektory znakowe jako dane wyjściowe, a nie liczby. Służą do formatowania etykiet osi i tym podobnych. Może chcesz po prostu pomnożyć przez 100?
Richie Cotton

Od 2020 scaleswer. Instrukcja 1.1.0 mówi: percent()jest na emeryturze; użyj label_percent()zamiast tego, co nie jest odpowiednie do formatowania liczb . Aby ręczne rozwiązanie było nadal aktualne
DzimitryM

78

Sprawdź scalespakiet. ggplot2Myślę, że kiedyś była częścią .

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

Wbudowana logika wykrywania dokładności powinna działać wystarczająco dobrze w większości przypadków.

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
Nie działa dla liczb ujemnych. percent(-0.1)produkujeNaN%
akhmed

1
@akhmed: To już zostało zgłoszone, poprawka jest dostępna, ale oczekuje na sprawdzenie: github.com/hadley/scales/issues/50 . Zauważ, że wydaje się działać dla więcej niż jednej liczby ujemnej:scales::percent(c(-0.1, -0.2))
krlmlr

Dzięki za link! Nie byłem pewien, czy to funkcja, czy błąd. W przypadku wielu liczb czasami działa, a czasami nie. Powiedz, scales::percent(c(-0.1,-0.1,-0.1))produkuje, "NaN%" "NaN%" "NaN%"ale twój przykład działa. Dla odniesienia innych, błąd nie został jeszcze naprawiony od dnia scales_0.2.4. Ponadto na dzień dzisiejszy odpowiednie żądanie ściągnięcia naprawiające go nie zostało jeszcze włączone do gałęzi głównej.
akhmed

35

Sprawdź percentfunkcję z formattablepakietu:

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

4
+1, pozwala to określić, ile cyfr ma zawierać, czego scales::percentw pierwszych dwóch odpowiedziach nie ma.
Sam Firke

3
+1, mimo że łatwo jest toczyć własną funkcję, wybranie liczby cyfr jest naprawdę przydatne.
Gang Su

10

Zrobiłem pewne testy porównawcze szybkości tych odpowiedzi i byłem zaskoczony, widząc percentw scalespakiecie tak reklamowanym, biorąc pod uwagę jego powolność. Wyobrażam sobie, że zaletą jest automatyczny detektor poprawnego formatowania, ale jeśli wiesz, jak wyglądają Twoje dane, wydaje się, że należy tego unikać.

Oto wyniki próby sformatowania listy 100 000 procent w (0,1) do wartości procentowej w 2 cyfrach:

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

sprintfWyłania się więc jako wyraźny zwycięzca, gdy chcemy dodać znak procentu. Z drugiej strony, jeśli chcemy tylko pomnożyć liczbę i zaokrąglić (przejść od proporcji do procentu bez "%", round()to najszybciej:

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


6

Oto moje rozwiązanie do zdefiniowania nowej funkcji (głównie po to, abym mógł bawić się Curry i Compose :-)):

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

3

Widząc, jak scalable::percentjuż wykazano, że jest najwolniejszy i Liliana Pacheco oferuje inne rozwiązanie, poszedłem dalej i spróbowałem porównać je z niektórymi innymi opcjami w oparciu o przykładowy zestaw Michaela:

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

Oto wyniki, które otrzymałem:

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

Nie mam jednak pojęcia, dlaczego moja krlmlr()i andrie1()wypadła o wiele gorzej niż w przykładzie Michaela Chirico. Jakieś wskazówki?


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

Ta funkcja może przekształcić dane na wartości procentowe według kolumn

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

Podstawowa arytmetyka jest wektoryzowana - wewnętrzna pętla for jest nieefektywna i niepotrzebna. Można zastąpić base2[, j] = base[ , j] * 100 / suma.c. Warto również zauważyć, że nie jest to dokładna odpowiedź na pytanie… pytanie dotyczy formatowania czegoś takiego jak 0.5„50,0%”, a nie wykonywania obliczeń…
Gregor Thomas

0

tidyverseWersja jest taka:

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

Wygląda jak zwykle schludnie.


0

Podstawa R

Zdecydowanie wolę korzystać z tego, sprintfco jest dostępne w bazie R.

sprintf("%0.1f%%", .7293827 * 100)
[1] "72.9%"

Szczególnie podoba mi się, sprintfponieważ można również wstawiać struny.

sprintf("People who prefer %s over %s: %0.4f%%", 
        "Coke Classic", 
        "New Coke",
        .999999 * 100)
[1] "People who prefer Coke Classic over New Coke: 99.9999%"

Jest to szczególnie przydatne w sprintfprzypadku takich rzeczy, jak konfiguracje baz danych; po prostu czytasz plik yaml, a następnie używasz sprintf, aby wypełnić szablon bez wielu nieprzyjemnych plików paste0.

Dłuższy motywujący przykład

Ten wzorzec jest szczególnie przydatny w raportach rmarkdown, gdy masz dużo tekstu i dużo wartości do zagregowania.

Konfiguracja / agregacja:

library(data.table) ## for aggregate

approval <- data.table(year = trunc(time(presidents)), 
                       pct = as.numeric(presidents) / 100,
                       president = c(rep("Truman", 32),
                                     rep("Eisenhower", 32),
                                     rep("Kennedy", 12),
                                     rep("Johnson", 20),
                                     rep("Nixon", 24)))
approval_agg <- approval[i = TRUE,
                         j = .(ave_approval = mean(pct, na.rm=T)), 
                         by = president]
approval_agg
#     president ave_approval
# 1:     Truman    0.4700000
# 2: Eisenhower    0.6484375
# 3:    Kennedy    0.7075000
# 4:    Johnson    0.5550000
# 5:      Nixon    0.4859091

Używanie sprintfz wektorami tekstu i liczb, wypisywanie cattylko do nowych linii.

approval_agg[, sprintf("%s approval rating: %0.1f%%",
                       president,
                       ave_approval * 100)] %>% 
  cat(., sep = "\n")
# 
# Truman approval rating: 47.0%
# Eisenhower approval rating: 64.8%
# Kennedy approval rating: 70.8%
# Johnson approval rating: 55.5%
# Nixon approval rating: 48.6%

Wreszcie, dla własnego samolubnego odniesienia, ponieważ mówimy o formatowaniu, tak robię przecinki z podstawą R:

30298.78 %>% round %>% prettyNum(big.mark = ",")
[1] "30,299"
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.