จะจัดรูปแบบตัวเลขเป็นเปอร์เซ็นต์ใน R ได้อย่างไร?


135

สิ่งหนึ่งที่ทำให้ฉันงงงวยในฐานะผู้สร้างใหม่โดยใช้ R คือการจัดรูปแบบตัวเลขเป็นเปอร์เซ็นต์สำหรับการพิมพ์

ยกตัวอย่างเช่นแสดงเป็น0.12345 12.345%ฉันมีวิธีแก้ปัญหาหลายประการสำหรับสิ่งนี้ แต่ดูเหมือนจะ "ใหม่โดยมิตร" ไม่ได้เลย ตัวอย่างเช่น:

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

คำถาม:มีฟังก์ชัน R ฐานสำหรับทำสิ่งนี้หรือไม่? นอกจากนี้ยังมีแพ็คเกจที่ใช้กันอย่างแพร่หลายซึ่งมีเครื่องห่อหุ้มที่สะดวกหรือไม่?


แม้จะมีการค้นหาสิ่งที่ต้องการในเรื่องนี้?format, ?formatCและ?prettyNumฉันยังไม่พบเสื้อคลุมสะดวกเหมาะสมในฐานอาร์ ??"percent"ไม่ได้ผลอะไรที่เป็นประโยชน์ library(sos); findFn("format percent")ส่งคืนการเข้าชม 1250 ครั้ง - อีกครั้งจึงไม่มีประโยชน์ ggplot2มีฟังก์ชันpercentแต่ไม่สามารถควบคุมความแม่นยำในการปัดเศษได้


5
sprintfดูเหมือนจะเป็นทางออกที่ชื่นชอบในรายชื่อส่งเมลและฉันไม่เห็นวิธีแก้ปัญหาที่ดีกว่านี้ ฟังก์ชั่นในตัวใด ๆ จะไม่เรียกง่ายกว่านี้ใช่ไหม?
michel-slm

1
ในมุมมองของฉันsprintfดีอย่างสมบูรณ์แบบสำหรับชุดย่อยของ R coders ที่เป็นโปรแกรมเมอร์ด้วย ฉันเขียนโค้ดหลายอย่างในชีวิตรวมถึง COBOL (ตัวสั่น) และ Fortran (แสดงอายุของฉัน) แต่ฉันไม่คิดว่าsprintfกฎการจัดรูปแบบชัดเจน (แปล: WTF?) และแน่นอนว่ากระดาษห่อหุ้มเฉพาะจะต้องเรียกได้ง่ายกว่า sprintf เช่นformat_percent(x=0.12345, digits=2)
Andrie

@hircus ฉันคิดว่ามันเป็นเรื่องธรรมดาที่สมควรได้รับฟังก์ชั่น curried สั้น ๆ ของตัวเอง โดยเฉพาะอย่างยิ่งปัญหาเกี่ยวกับ Sweave โดยที่ \ Sexpr {sprintf (% 1.2f %% ", myvar)} นั้นน่าเกลียดกว่า \ Sexpr {pct (myvar)} มากหรืออะไรก็ตามที่จะเป็นฟังก์ชันที่สั้นกว่า
Ari B. Friedman

2
การเรียนรู้ที่จะใช้เครื่องมือที่เหมาะสมไม่ใช่สิ่งที่เราควรคาดหวังให้ผู้ใช้มุ่งมั่นหรือ ผมหมายถึงการเรียนรู้การใช้งานsprintf()เป็นเวลาที่แทบจะไม่บริโภคกว่าหาว่าแพคเกจfooformat_percent()มี จะเกิดอะไรขึ้นถ้าผู้ใช้ไม่ต้องการจัดรูปแบบเป็นเปอร์เซ็นต์ แต่เป็นอย่างอื่นที่คล้ายกัน? พวกเขาต้องหากระดาษห่ออื่น ในระยะยาวการเรียนรู้เครื่องมือพื้นฐานจะเป็นประโยชน์
Gavin Simpson

1
มีปัญหาเล็กน้อยนั่น%คืออักขระข้อคิดเห็นใน LaTeX ซึ่งเป็นรูปแบบการรายงาน "เริ่มต้น" สำหรับ R ดังนั้นในขณะที่อาจเป็นประโยชน์สำหรับการติดฉลากกราฟ แต่ต้องใช้ความระมัดระวังหากต้องกวาดตัวเลขที่จัดรูปแบบ
James

คำตอบ:


118

แม้ในภายหลัง:

ตามที่ระบุโดย @DzimitryM percent()ได้รับการ "เกษียณ" label_percent()ซึ่งเป็นคำพ้องความหมายของpercent_format()ฟังก์ชันเก่า

label_percent() ส่งคืนฟังก์ชันดังนั้นในการใช้งานคุณต้องมีวงเล็บเสริมอีกหนึ่งคู่

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

ปรับแต่งสิ่งนี้โดยเพิ่มอาร์กิวเมนต์ภายในวงเล็บชุดแรก

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

การอัปเดตหลายปีต่อมา:

วันนี้มีpercentฟังก์ชั่นในscalesแพ็คเกจตามที่ระบุไว้ในคำตอบของ krlmlr ใช้แทนน้ำยารีดมือของฉัน


ลองสิ่งที่ชอบ

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

กับการใช้งานเช่น

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

(หากคุณต้องการเปลี่ยนรูปแบบจาก"f"เป็น"g")


2
ใช่มันใช้งานได้และเป็นเวอร์ชันทั่วไปกว่าเล็กน้อยของวิธีแก้ปัญหาที่ฉันระบุในคำถาม แต่คำถามที่แท้จริงของฉันคือสิ่งนี้มีอยู่ในฐาน R หรือไม่
Andrie

ใช้ได้ผลกับฉันในการแสดงรายการเปอร์เซ็นต์ แต่การแทนที่ "x" ด้วย "เปอร์เซ็นต์ (x)" ในคำสั่งทางสถิติหรือการสร้างกราฟจะทำให้เกิดข้อความแสดงข้อผิดพลาด
rolando2

@ rolando2 ทั้งคำตอบของฉันและคำตอบของ krlmlr จะส่งกลับเวกเตอร์อักขระเป็นผลลัพธ์ไม่ใช่ตัวเลข มีไว้สำหรับการจัดรูปแบบป้ายแกนและอื่น ๆ บางทีคุณแค่อยากคูณด้วย 100?
Richie Cotton

ในฐานะที่เป็น 2020 scalesเวอร์ชั่น 1.1.0 คู่มือบอก: percent()ถูกยกเลิก; โปรดใช้label_percent()แทนซึ่งไม่เหมาะสำหรับการจัดรูปแบบตัวเลข เพื่อให้การแก้ปัญหาด้วยมือยังคงมีความเกี่ยวข้อง
DzimitryM

74

ตรวจสอบscalesแพ็คเกจ มันเคยเป็นส่วนหนึ่งของggplot2ฉันคิดว่า

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

ตรรกะในตัวสำหรับการตรวจจับความแม่นยำควรทำงานได้ดีเพียงพอสำหรับกรณีส่วนใหญ่

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
ใช้ไม่ได้กับจำนวนลบ percent(-0.1)ผลิตNaN%
akhmed

1
@akhmed: นี้ได้รับรายงานแล้วการแก้ไขสามารถใช้ได้ แต่รอการตรวจสอบ: github.com/hadley/scales/issues/50 โปรดทราบว่าดูเหมือนว่าจะใช้ได้กับจำนวนลบมากกว่าหนึ่งจำนวน:scales::percent(c(-0.1, -0.2))
krlmlr

ขอบคุณสำหรับลิงค์! ฉันไม่แน่ใจว่าเป็นคุณสมบัติหรือข้อบกพร่อง สำหรับหลาย ๆ หมายเลขบางครั้งก็ใช้งานได้และบางครั้งก็ใช้ไม่ได้ พูดว่าscales::percent(c(-0.1,-0.1,-0.1))สร้าง"NaN%" "NaN%" "NaN%"แต่ตัวอย่างของคุณได้ผล scales_0.2.4สำหรับการอ้างอิงของผู้อื่นข้อผิดพลาดยังไม่คงที่ของ นอกจากนี้ ณ วันนี้คำขอดึงที่เกี่ยวข้องที่แก้ไขคำขอนั้นยังไม่รวมเข้ากับสาขาหลัก
akhmed

34

ตรวจสอบpercentฟังก์ชั่นจากformattableแพ็คเกจ:

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

4
+1 ซึ่งช่วยในการระบุจำนวนตัวเลขที่จะรวมซึ่งscales::percentในสองคำตอบแรกไม่มี
Sam Firke

3
+1 แม้ว่ามันจะค่อนข้างง่ายในการหมุนฟังก์ชันของคุณเอง แต่การเลือกจำนวนหลักก็มีประโยชน์มาก
Gang Su

10

ฉันทำการเปรียบเทียบเพื่อความเร็วในคำตอบเหล่านี้และรู้สึกประหลาดใจที่เห็นpercentในscalesแพ็คเกจนั้นได้รับการขนานนามอย่างมากเนื่องจากความเฉื่อยชา ฉันคิดว่าข้อดีคือเครื่องตรวจจับอัตโนมัติสำหรับการจัดรูปแบบที่เหมาะสม แต่ถ้าคุณรู้ว่าข้อมูลของคุณมีลักษณะอย่างไรก็ควรหลีกเลี่ยง

นี่คือผลลัพธ์จากการพยายามจัดรูปแบบรายการ 100,000 เปอร์เซ็นต์ใน (0,1) เป็นเปอร์เซ็นต์ใน 2 หลัก:

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กลายเป็นผู้ชนะที่ชัดเจนเมื่อเราต้องการเพิ่มเครื่องหมายเปอร์เซ็นต์ ในทางกลับกันหากเราต้องการคูณจำนวนและปัดเศษเท่านั้น (เปลี่ยนจากสัดส่วนเป็นเปอร์เซ็นต์โดยไม่มี "%" round()จะเร็วที่สุด:

# 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

คุณสามารถใช้แพ็คเกจเครื่องชั่งสำหรับการดำเนินการนี้เท่านั้น (โดยไม่ต้องโหลดด้วยต้องใช้หรือไลบรารี)

scales::percent(m)

1
จะให้ความแม่นยำของจำนวนหลักได้อย่างไร?
Elmex80s

6

นี่คือคำตอบของฉันสำหรับการกำหนดฟังก์ชันใหม่ (ส่วนใหญ่ฉันสามารถเล่นกับ Curry และ Compose ได้ :-)):

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

3

เมื่อเห็นว่าscalable::percentได้แสดงให้เห็นแล้วว่าช้าที่สุดและ Liliana Pacheco ก็เสนอวิธีแก้ปัญหาอื่นฉันจึงพยายามเปรียบเทียบกับตัวเลือกอื่น ๆ ตามตัวอย่างที่ 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())

นี่คือผลลัพธ์ที่ฉันได้รับ:

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

ฉันไม่รู้เลยว่าทำไมฉันkrlmlr()ถึงandrie1()ทำได้แย่กว่าในตัวอย่างของ MichaelChirico เบาะแสใด ๆ ?


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

ฟังก์ชันนี้สามารถแปลงข้อมูลเป็นเปอร์เซ็นต์ตามคอลัมน์

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

เลขคณิตพื้นฐานเป็นเวกเตอร์ - ด้านในสำหรับลูปไม่มีประสิทธิภาพและไม่จำเป็น สามารถแทนที่ด้วยbase2[, j] = base[ , j] * 100 / suma.c. นอกจากนี้ที่น่าสังเกตว่านี่ไม่ใช่คำตอบของคำถามอย่างแน่นอน ... คำถามเกี่ยวกับการจัดรูปแบบบางอย่างเช่น0.5"50.0%" ไม่ใช่การคำนวณ ...
Gregor Thomas

0

tidyverseรุ่นนี้คือ:

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

ดูเป็นระเบียบเรียบร้อยเช่นเคย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.