根据小数点前位数的不同舍入规则

Different rounding rule based on number of digits before the decimal

假设我们有以下数值向量:

y=c(0.111,1.11,11.1,111.1)

我希望它四舍五入如下:

0.11,1.11,11.1,111  

这个规则很简单:一共要三位数!但如果数字很大(例如 1111 或 11111),则保留所有数字,但不带任何小数。

当然,必须有一个更简单的解决方案?:

lapply(y,function(x){
  ifelse(nchar(as.integer(x))<1,round(x,digits=3),
         ifelse(nchar(as.integer(x))==1,round(x,digits=2),
                ifelse(nchar(as.integer(x))==2,round(x,digits=1),
                       ifelse(nchar(as.integer(x))>2,round(x,digits=0)
                       ))))
}
)

这个有用吗?

y <- c(0.111,1.11,11.1,111.1,1111,11111)
(ry <- gsub("0+$","",round(y*100)/100))
## [1] "0.11"  "1.11"  "11.1"  "111.1" "1111"  "11111"
as.numeric(ry)
## [1]     0.11     1.11    11.10   111.10  1111.00 11111.00

我一般不喜欢转换为字符的解决方案,但它似乎可以做你想做的事......(请注意,这个 rounds 是你想要的,但是打印的数值重新包括零值:cat(ry,"\n") 将按原样打印出数字)

只需使用signif:

y <- c(0.111,1.11,11.1,111.1,1111)
signif(y, pmax(3,trunc(log10(y)+1)))
#[1]    0.111    1.110   11.100  111.000 1111.000

pmax(...) 部分说明了数字在小数点前有 4 位或更多位的情况(log10 只是计算这些数字的一种方式)。

不清楚问题是关于漂亮打印(结果是字符)还是舍入(结果是数字)。

如果问题是关于转换为漂亮打印的字符,可以使用 sprintf() 函数。

sprintf() 函数理解转换说明符 g,其中可以指定 有效数字 的数目。但是,它并未涵盖整个值范围。因此,必须根据 y:

的大小使用不同的转换说明符
y <- c(0.0111, 0.111, 1.11, 11.1, 111.1, 1111, 11111)
sprintf(c("%3.2f", "%3.3g", "%3.0f")[cut(y, c(0, 1, 1000, Inf))], y)
[1] "0.01"  "0.11"  "1.11"  "11.1"  "111"   "1111"  "11111"

如果问题是关于数值的舍入:

round(y, c(2, 1, 0)[cut(y, c(0, 10, 100, Inf))])
[1]     0.01     0.11     1.11    11.10   111.00  1111.00 11111.00

说明

c("%3.2f", "%3.3g", "%3.0f")[cut(y, c(0, 1, 1000, Inf))]

是嵌套 ifelse() 的替代品,以便为每个 y:

选择正确的转换说明符
[1] "%3.2f" "%3.2f" "%3.3g" "%3.3g" "%3.3g" "%3.0f" "%3.0f"

cut() 将数字转换为因子。它将 y 的范围划分为区间,并根据它们属于哪个区间对 y 中的值进行编码。最左边的间隔对应于第一级,下一个最左边的间隔对应于第二级,依此类推。 (来自 help("cut"))。

然后,根据y的值,使用级别数对转换说明符向量进行子集化,以选择合适的说明符。

在写解释的时候,有一个稍微不同的方法。我们可以直接使用级别的标签,而不是使用级别编号对转换说明符的向量进行子集化:

sprintf(as.character(cut(y, c(0, 1, 1000, Inf), labels = c("%3.2f", "%3.3g", "%3.0f"))), y)
[1] "0.01"  "0.11"  "1.11"  "11.1"  "111"   "1111"  "11111"