如何格式化一个数字的百分比在 R?

作为一个 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中搜索了类似的东西,但是我还没有在基础 R 中找到一个合适的方便的包装,??"percent"没有产生任何有用的东西。library(sos); findFn("format percent")返回1250个点击-所以同样没有用。ggplot2有一个功能 percent,但这并不能控制舍入精度。

335329 次浏览

下面是我定义一个新函数的解决方案(主要是为了我可以玩 Curry 和 Compose: ——) :

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

甚至更晚:

正如@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"

几年后的最新消息:

如今在 scales包中有一个 percent函数,正如 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"。)

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

我对这些答案的速度做了一些基准测试,很惊讶地看到 scales软件包中的 percent被如此吹捧,因为它的速度太慢了。我想它的优势在于自动检测正确的格式,但是如果您知道您的数据看起来是什么样子,那么似乎可以很清楚地避免使用它。

下面是试图将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()
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)
}

查看 formattable软件包中的 percent函数:

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

此函数可以按列将数据转换为百分比

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

您可以仅为此操作使用 Scale 包(不需要加载带有要求或库的包)

scales::percent(m)

tidyverse版本是这样的:

> library(dplyr)
> library(scales)


> set.seed(1)
> m <- runif(5)
> dt <- as.data.frame(m)


> dt %>% mutate(perc=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%

看起来和平时一样整洁。

R 基地

我更喜欢使用 sprintf,它可以在基地 R。

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

我特别喜欢 sprintf,因为你也可以插入字符串。

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

在诸如数据库配置之类的事情上使用 sprintf尤其有用; 您只需读入 yaml 文件,然后使用 sprintf 填充模板,而不需要使用大量令人讨厌的 paste0

更长的激励例子

当您有大量文本和大量值要聚合时,此模式对于 rmarkdown 报告特别有用。

设置/聚合:

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

使用带有文本和数字向量的 sprintf,输出到 cat只是为了换行。

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%

最后,为了我自己的私利,既然我们在讨论格式,这就是我如何使用基础 R 的逗号:

30298.78 %>% round %>% prettyNum(big.mark = ",")
[1] "30,299"