Bagaimana memformat angka sebagai persentase dalam R?

135

Salah satu hal yang digunakan untuk membingungkan saya sebagai yang baru ke R adalah bagaimana memformat angka sebagai persentase untuk dicetak.

Misalnya, tampilkan 0.12345sebagai 12.345%. Saya memiliki sejumlah solusi untuk ini, tetapi tidak satu pun dari ini tampaknya "baru oleh ramah". Sebagai contoh:

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

Pertanyaan: Apakah ada fungsi dasar R untuk melakukan ini? Atau, apakah ada paket yang digunakan secara luas yang menyediakan pembungkus yang nyaman?


Meskipun mencari sesuatu seperti ini ?format, ?formatCdan ?prettyNum, saya belum menemukan pembungkus nyaman yang sesuai di basis R. ??"percent"tidak menghasilkan sesuatu yang berguna. library(sos); findFn("format percent")mengembalikan 1.250 hit - jadi sekali lagi tidak berguna. ggplot2memiliki fungsi percenttetapi ini tidak memberikan kontrol atas akurasi pembulatan.

Andrie
sumber
5
sprintftampaknya menjadi solusi favorit di milis, dan saya belum melihat solusi yang lebih baik. Fungsi bawaan apa pun tidak akan lebih mudah untuk dipanggil, bukan?
michel-slm
1
Dalam pandangan saya sprintfbaik-baik saja untuk subset dari coders R yang juga kebetulan programmer. Saya memiliki banyak kode dalam hidup saya, termasuk COBOL (gemetar) dan fortran (menunjukkan usia saya). Tapi saya tidak menganggap sprintfaturan format jelas (terjemahan: WTF?). Dan tentu saja bungkus khusus harus lebih mudah dihubungi daripada sprintf, misalnya:format_percent(x=0.12345, digits=2)
Andrie
@ Shirus Saya pikir itu cukup umum sehingga layak untuk fungsi kari pendeknya sendiri. Ini terutama masalah dengan Sweave, di mana \ Sexpr {sprintf (% 1.2f %% ", myvar)} jauh lebih jelek daripada \ Sexpr {pct (myvar)} atau apa pun fungsi yang lebih pendek.
Ari B. Friedman
2
Bukankah belajar menggunakan alat yang sesuai adalah sesuatu yang harus kita harapkan agar diperjuangkan oleh pengguna? Maksud saya, belajar menggunakan sprintf()hampir tidak memakan waktu banyak daripada mengetahui bahwa paket foo berisi format_percent(). Apa yang terjadi jika pengguna kemudian tidak ingin memformat sebagai persen tetapi sesuatu yang serupa? Mereka perlu menemukan pembungkus lain. Dalam jangka panjang, pembelajaran alat dasar akan bermanfaat.
Gavin Simpson
1
Ada sedikit masalah dalam hal itu %adalah karakter komentar di LaTeX, yang merupakan format pelaporan "default" untuk R. Jadi, sementara itu mungkin berguna untuk memberi label pada grafik, kehati-hatian harus diperhatikan jika nomor yang diformat akan di-Sweave.
James

Jawaban:

118

Bahkan kemudian:

Seperti yang ditunjukkan oleh @DzimitryM, percent()telah "pensiun" yang mendukung label_percent(), yang merupakan sinonim untuk percent_format()fungsi lama .

label_percent() mengembalikan fungsi, jadi untuk menggunakannya, Anda membutuhkan sepasang tanda kurung tambahan.

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

Kustomisasi ini dengan menambahkan argumen di dalam set kurung pertama.

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

Pembaruan, beberapa tahun kemudian:

Saat ini ada percentfungsi dalam scalespaket, seperti yang didokumentasikan dalam jawaban krlmlr. Gunakan itu alih-alih solusi linting tangan saya.


Coba sesuatu seperti

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

Dengan penggunaan, misalnya,

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

(Jika Anda mau, ubah format dari "f"menjadi "g".)

Richie Cotton
sumber
2
Ya, ini berfungsi, dan merupakan versi yang sedikit lebih umum dari solusi yang saya berikan dalam pertanyaan. Tetapi pertanyaan saya yang sebenarnya adalah apakah ini ada di basis R atau tidak.
Andrie
Bekerja untuk saya dalam daftar persen, tetapi mengganti "x" dengan "persen (x)" dalam perintah statistik atau grafik menghasilkan pesan kesalahan.
rolando2
@ rolando2 Baik jawaban saya dan krlmlr mengembalikan vektor karakter sebagai output, bukan angka. Mereka untuk memformat label sumbu dan sejenisnya. Mungkin Anda hanya ingin mengalikan 100?
Richie Cotton
Pada 2020 scalesver. Manual 1.1.0 memberitahu: percent()sudah pensiun; silakan gunakan label_percent()sebagai gantinya, yang tidak cocok untuk pemformatan angka . Sehingga solusi linting masih relevan
DzimitryM
74

Lihatlah scalespaketnya. Dulu bagian dari ggplot2, saya pikir.

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

Logika bawaan untuk mendeteksi presisi harus bekerja cukup baik untuk sebagian besar kasus.

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
sumber
2
Tidak berfungsi untuk angka negatif. percent(-0.1)menghasilkanNaN%
akhmed
1
@akhmed: Ini sudah dilaporkan, perbaikan sudah tersedia tetapi menunggu tinjauan: github.com/hadley/scales/issues/50 . Perhatikan bahwa tampaknya berfungsi untuk lebih dari satu angka negatif:scales::percent(c(-0.1, -0.2))
krlmlr
Terima kasih untuk tautannya! Saya tidak yakin apakah ini fitur atau bug. Untuk beberapa nomor terkadang berfungsi dan terkadang tidak. Katakan, scales::percent(c(-0.1,-0.1,-0.1))hasilkan "NaN%" "NaN%" "NaN%"tetapi contoh Anda tidak bekerja. Untuk referensi orang lain, bug belum diperbaiki pada scales_0.2.4. Juga, pada hari ini, permintaan tarik yang terkait untuk memperbaikinya belum bergabung ke cabang utama.
akhmed
34

Lihat percent fungsi dari formattablepaket:

library(formattable)
x <- c(0.23, 0.95, 0.3)
percent(x)
[1] 23.00% 95.00% 30.00%
Liliana Pacheco
sumber
4
+1, ini memungkinkan untuk menentukan berapa digit untuk dimasukkan, yang scales::percentdalam dua jawaban pertama tidak.
Sam Firke
3
+1, meskipun cukup mudah untuk memutar fungsi Anda sendiri, memungkinkan memilih jumlah digit sangat berguna.
Gang Su
10

Saya melakukan pembandingan untuk kecepatan pada jawaban-jawaban ini dan terkejut melihat percentdalam scalespaket yang disebut-sebut begitu, mengingat lambannya. Saya membayangkan keuntungannya adalah pendeteksi otomatis untuk pemformatan yang tepat, tetapi jika Anda tahu seperti apa data Anda, tampaknya jelas harus dihindari.

Berikut adalah hasil dari mencoba memformat daftar 100.000 persentase dalam (0,1) menjadi persentase dalam 2 digit:

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

Jadi sprintfmuncul sebagai pemenang yang jelas ketika kita ingin menambahkan tanda persen. Di sisi lain, jika kita hanya ingin mengalikan angka dan putaran (beralih dari proporsi menjadi persen tanpa "%", maka itu round()adalah yang tercepat:

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

Anda dapat menggunakan paket timbangan hanya untuk operasi ini (tanpa memuatnya dengan persyaratan atau pustaka)

scales::percent(m)
בנימן הגלילי
sumber
1
Bagaimana cara memberi akurasi jumlah digit?
Elmex80s
6

Inilah solusi saya untuk mendefinisikan fungsi baru (sebagian besar agar saya bisa bermain-main dengan Curry and Compose :-)):

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

Melihat bagaimana scalable::percenttelah terbukti paling lambat dan Liliana Pacheco menawarkan solusi lain, saya melanjutkan dan mencoba membandingkannya dengan beberapa opsi lain berdasarkan contoh yang ditetapkan 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())

Ini adalah hasil yang saya dapatkan:

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

Saya tidak tahu, mengapa krlmlr()dan andrie1()kinerja saya jauh lebih buruk daripada dalam contoh MichaelChirico. Ada petunjuk?

matt_jay
sumber
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)
}
suara ringan
sumber
0

Fungsi ini dapat mengubah data menjadi persentase menurut kolom

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
sumber
Aritmatika dasar adalah vektor --- bagian dalam untuk loop tidak efisien dan tidak perlu. Dapat diganti dengan base2[, j] = base[ , j] * 100 / suma.c. Juga patut dicatat bahwa ini bukan jawaban untuk pertanyaan ... pertanyaannya adalah tentang memformat sesuatu seperti 0.5"50.0%", bukan tentang melakukan perhitungan ...
Gregor Thomas
0

The tidyverseversi ini:

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

Terlihat rapi seperti biasa.

Giacomo
sumber