如何在R中将数字格式化为百分比?


135

令我困惑的R之一是如何格式化数字以百分比形式打印。

例如,显示0.1234512.345%。我对此有很多解决方法,但是这些方法似乎都不是“ newby friendly”。例如:

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但不能控制舍入精度。


5
sprintf似乎是邮件列表上最喜欢的解决方案,而且我还没有看到更好的解决方案。无论如何,任何内置函数都不会简单得多,对吧?
michel-slm

1
在我看来,sprintf对于恰好是程序员的R编码器子集来说,这是完全可以的。我一生中编写了很多代码,包括COBOL(颤抖)和fortran(显示我的年龄)。但是我认为sprintf格式设置规则不明显(翻译:WTF?)。当然,专用的包装器必须比sprintf调用起来容易,例如:format_percent(x=0.12345, digits=2)
Andrie

@hircus我认为它很普遍,应该得到其自己的简短咖喱函数。尤其是Sweave的问题,其中\ Sexpr {sprintf(%1.2f %%“,myvar)}比\ Sexpr {pct(myvar)}或任何更短的函数都要难看得多
Ari B. Friedman

2
是不是在学习使用适当的工具,我们应该期望用户为之奋斗?我的意思是,学习使用sprintf()几乎没有发现foo包包含的时间format_percent()。如果用户随后不想格式化为百分比,但又希望类似的格式,会发生什么?他们需要找到另一个包装器。从长远来看,基础工具将是有益的。
加文·辛普森,

1
这里有一个小问题,那%就是LaTeX中的注释字符,它是R的“默认”报告格式。因此,尽管它对于标记图形很有用,但是如果要对格式化的数字进行转换,必须格外小心。
詹姆斯,

Answers:


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"

几年后的更新:

如今percentscales软件包已包含一个功能,如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”替换为“ percent(x)”会产生错误消息。
rolando2 2014年

@ 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,2015年

1
@akhmed:已经有报道,修复可用,但有待审查:github.com/hadley/scales/issues/50。请注意,它似乎适用于多个负数:scales::percent(c(-0.1, -0.2))
krlmlr 2015年

感谢您的链接!我不确定这是功能还是错误。对于多个数字,它有时有效,有时却无效。说,scales::percent(c(-0.1,-0.1,-0.1))产生,"NaN%" "NaN%" "NaN%"但是您的示例确实起作用。供其他人参考,该错误截至尚未修复scales_0.2.4。同样,从今天开始,解决该问题的相应拉取请求尚未合并到主分支中。
akhmed 2015年

34

percentformattable包中检查功能:

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 '16

3
+1,尽管滚动自己的功能非常容易,但允许选择位数确实很有用。
Gang Su

10

我对这些答案的速度进行了一些基准测试,但由于它的呆滞,令我惊讶的是它percentscales包装中如此吹捧。我想它的优点是它的自动检测器可以正确格式化,但是如果您知道数据看起来像什么,那么显然可以避免。

以下是尝试将(0,1)中的100,000个百分比的列表格式设置为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()


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

基本算术是矢量化的-内部for循环效率低下且不必要。可以替换为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%

看起来像往常一样整洁。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.