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.12345
jako 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
, ?formatC
i ?prettyNum
muszę 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. ggplot2
ma funkcję, percent
ale nie daje kontroli nad dokładnością zaokrąglania.
źródło
sprintf
wydaje 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?sprintf
jest 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żamsprintf
reguł 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)
sprintf()
nie zajmuje więcej czasu niż odkrycie, że pakiet foo zawieraformat_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.%
ż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.Odpowiedzi:
Nawet później:
Jak wskazał @DzimitryM,
percent()
został „wycofany” na rzeczlabel_percent()
, co jest synonimem starejpercent_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
percent
funkcji wscales
pakiecie, 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"
.)źródło
scales
wer. Instrukcja 1.1.0 mówi:percent()
jest na emeryturze; użyjlabel_percent()
zamiast tego, co nie jest odpowiednie do formatowania liczb . Aby ręczne rozwiązanie było nadal aktualneSprawdź
scales
pakiet.ggplot2
Myś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%"
źródło
percent(-0.1)
produkujeNaN%
scales::percent(c(-0.1, -0.2))
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 dniascales_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.Sprawdź
percent
funkcję zformattable
pakietu:library(formattable) x <- c(0.23, 0.95, 0.3) percent(x) [1] 23.00% 95.00% 30.00%
źródło
scales::percent
w pierwszych dwóch odpowiedziach nie ma.Zrobiłem pewne testy porównawcze szybkości tych odpowiedzi i byłem zaskoczony, widząc
percent
wscales
pakiecie 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()
sprintf
Wył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()
źródło
Możesz użyć pakietu scales tylko do tej operacji (bez ładowania go wymaganiem lub biblioteką)
źródło
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%%"))
źródło
Widząc, jak
scalable::percent
już 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()
iandrie1()
wypadła o wiele gorzej niż w przykładzie Michaela Chirico. Jakieś wskazówki?źródło
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) }
źródło
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) }
źródło
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 jak0.5
„50,0%”, a nie wykonywania obliczeń…tidyverse
Wersja 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.
źródło
Podstawa R
Zdecydowanie wolę korzystać z tego,
sprintf
co jest dostępne w bazie R.sprintf("%0.1f%%", .7293827 * 100) [1] "72.9%"
Szczególnie podoba mi się,
sprintf
ponieważ 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
sprintf
przypadku 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ówpaste0
.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
sprintf
z wektorami tekstu i liczb, wypisywaniecat
tylko 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"
źródło