【问题标题】:How to round up to the nearest 10 (or 100 or X)?如何四舍五入到最接近的 10(或 100 或 X)?
【发布时间】:2011-09-21 14:49:34
【问题描述】:

我正在编写一个函数来绘制数据。我想为 y 轴 max 指定一个大于数据集最大值的整数。

具体来说,我想要一个函数foo,它执行以下操作:

foo(4) == 5
foo(6.1) == 10 #maybe 7 would be better
foo(30.1) == 40
foo(100.1) == 110 

我已经做到了

foo <- function(x) ceiling(max(x)/10)*10

用于四舍五入到最接近的 10,但这不适用于任意舍入间隔。

在 R 中有没有更好的方法来做到这一点?

【问题讨论】:

  • R 绘图时的默认行为是将绘图限制设置为超出每个方向的数据范围约 4%。如果这对你不满意,也许只是写一些更高或更低百分比的东西?
  • @joran 感谢您提供信息,但我想要多个具有相同轴限制和刻度的图,我不确定这有什么帮助。
  • 好吧,我有点在黑暗中摸索,因为我不知道所有的背景。如果您只添加另一个参数 X 并将两个 10 替换为 X,您的 foo 将四舍五入到最接近的 X。或者您可以使用 faceting。
  • 您在寻找?pretty 吗?
  • 为什么是foo(4)==5 而不是10

标签: r rounding


【解决方案1】:

plyr 库有一个函数round_any,它非常通用,可以进行各种舍入。例如

library(plyr)
round_any(132.1, 10)               # returns 130
round_any(132.1, 10, f = ceiling)  # returns 140
round_any(132.1, 5, f = ceiling)   # returns 135

【讨论】:

【解决方案2】:

如果您只想四舍五入到最接近的 10 次幂,那么只需定义:

roundUp <- function(x) 10^ceiling(log10(x))

这实际上也适用于 x 是向量时:

> roundUp(c(0.0023, 3.99, 10, 1003))
[1] 1e-02 1e+01 1e+01 1e+04

..但是如果你想四舍五入到一个“好”的数字,你首先需要定义一个“好”的数字是什么。下面让我们将“nice”定义为具有从 1 到 10 的不错基值的向量。默认设置为偶数加 5。

roundUpNice <- function(x, nice=c(1,2,4,5,6,8,10)) {
    if(length(x) != 1) stop("'x' must be of length 1")
    10^floor(log10(x)) * nice[[which(x <= 10^floor(log10(x)) * nice)[[1]]]]
}

当 x 是向量时,上述方法不起作用 - 现在晚上太晚了 :)

> roundUpNice(0.0322)
[1] 0.04
> roundUpNice(3.22)
[1] 4
> roundUpNice(32.2)
[1] 40
> roundUpNice(42.2)
[1] 50
> roundUpNice(422.2)
[1] 500

[[编辑]]

如果问题是如何四舍五入到指定的最接近值(如 10 或 100),那么 James answer 似乎最合适。我的版本允许您取任何值并自动将其四舍五入到一个合理的“好”值。上面“nice”向量的其他一些不错的选择是:1:10, c(1,5,10), seq(1, 10, 0.1)

如果您的绘图中有一个值范围,例如[3996.225, 40001.893],那么自动方式应该同时考虑范围的大小和数字的大小。而作为noted by Hadleypretty() 函数可能就是你想要的。

【讨论】:

  • Vectorize(roundUpNice) 很快 =) +1 无论如何。
  • 我想知道如果原始值较低,是否有办法将 roundUp 函数编辑为最终结果的一半?例如,101-500 之间的数字会舍入到 500,而数字 501-999 会舍入到 1000?而不是四舍五入到 1000?
  • 只需更改漂亮的矢量:roundUpNice(501, nice=c(5, 10)) # 1000
  • 只是一个警告,因为您在函数中使用对数,我会遇到 2 种情况,一种情况是 x &lt; 0 并在日志中应用 - x,然后再将 - 放回去。我还要为x = 0 的情况添加一个例外
  • 漂亮的功能非常适合我的需求(在图形的 X 轴上设置漂亮的限制)
【解决方案3】:

如果数字参数为负数,R 中的 round 函数赋予其特殊含义。

round(x, 数字 = 0)

四舍五入到负数意味着四舍五入到十的幂,例如round(x, digits = -2) 舍入到最接近的百位。

这意味着像下面这样的函数可以非常接近你的要求。

foo <- function(x)
{
    round(x+5,-1)
}

输出如下所示

foo(4)
[1] 10
foo(6.1)
[1] 10
foo(30.1)
[1] 40
foo(100.1)
[1] 110

【讨论】:

  • 太棒了!你的答案应该被标记为正确的!
  • +1 。但是,@Alessandro 我的answer 中的函数更加通用:您可以将 ANY 数字向上舍入 ORANY 区间。
  • @theforestecologist 谢谢你的提示!就个人喜好而言,我通常更喜欢语言内置解决方案而不是自定义解决方案。
【解决方案4】:

如果你在 round() 的数字参数中添加一个负数,R 会将它四舍五入为 10、100 等的倍数。

    round(9, digits = -1) 
    [1] 10    
    round(89, digits = -1) 
    [1] 90
    round(89, digits = -2) 
    [1] 100

【讨论】:

    【解决方案5】:

    怎么样:

    roundUp <- function(x,to=10)
    {
      to*(x%/%to + as.logical(x%%to))
    }
    

    这给出了:

    > roundUp(c(4,6.1,30.1,100.1))
    [1]  10  10  40 110
    > roundUp(4,5)
    [1] 5
    > roundUp(12,7)
    [1] 14
    

    【讨论】:

    • @daroczig 这个问题有点令人困惑,我写这篇文章的重点是“任意 X”的要求,但显然所有的预期值都不能通过“一次四舍五入到最接近的 X”产生解决方案。看来 OP 想要为轴生成值,所以 pretty 可能是最好的选择。
    • 这个聚会显然迟到了,但to * ceiling(x / to) 不是更干净吗?
    【解决方案6】:

    将任意数字向上/向下舍入到任意间隔

    您可以使用 模运算符 %% 轻松地将数字四舍五入到特定区间。

    功能:

    round.choose <- function(x, roundTo, dir = 1) {
      if(dir == 1) {  ##ROUND UP
        x + (roundTo - x %% roundTo)
      } else {
        if(dir == 0) {  ##ROUND DOWN
          x - (x %% roundTo)
        }
      }
    }
    

    示例:

    > round.choose(17,5,1)   #round 17 UP to the next 5th
    [1] 20
    > round.choose(17,5,0)   #round 17 DOWN to the next 5th
    [1] 15
    > round.choose(17,2,1)   #round 17 UP to the next even number
    [1] 18
    > round.choose(17,2,0)   #round 17 DOWN to the next even number
    [1] 16
    

    工作原理:

    模运算符%% 确定第一个数字除以第二个数字的余数。在您感兴趣的数字上添加或减去此间隔基本上可以将该数字“四舍五入”到您选择的间隔。

    > 7 + (5 - 7 %% 5)       #round UP to the nearest 5
    [1] 10
    > 7 + (10 - 7 %% 10)     #round UP to the nearest 10
    [1] 10
    > 7 + (2 - 7 %% 2)       #round UP to the nearest even number
    [1] 8
    > 7 + (100 - 7 %% 100)   #round UP to the nearest 100
    [1] 100
    > 7 + (4 - 7 %% 4)       #round UP to the nearest interval of 4
    [1] 8
    > 7 + (4.5 - 7 %% 4.5)   #round UP to the nearest interval of 4.5
    [1] 9
    
    > 7 - (7 %% 5)           #round DOWN to the nearest 5
    [1] 5
    > 7 - (7 %% 10)          #round DOWN to the nearest 10
    [1] 0
    > 7 - (7 %% 2)           #round DOWN to the nearest even number
    [1] 6
    

    更新:

    方便的 2 参数版本:

    rounder <- function(x,y) {
      if(y >= 0) { x + (y - x %% y)}
      else { x - (x %% abs(y))}
    }
    

    正值yroundUp,负值yroundDown

     # rounder(7, -4.5) = 4.5, while rounder(7, 4.5) = 9.
    

    或者....

    根据标准舍入规则自动向上舍入向下的功能:

    Round <- function(x,y) {
      if((y - x %% y) <= x %% y) { x + (y - x %% y)}
      else { x - (x %% y)}
    }
    

    如果x 值是&gt; 在舍入值y 的后续实例之间的中间值,则自动舍入:

    # Round(1.3,1) = 1 while Round(1.6,1) = 2
    # Round(1.024,0.05) = 1 while Round(1.03,0.05) = 1.05
    

    【讨论】:

    • 有人问我如何在 Excel 中将Round 转换为 VBA:Function ROUND(x,y) 'Function that automatically rounds UP or DOWN based on standard rounding rules. 'Automatically rounds up if the "x" value is &gt; halfway between subsequent instances of the rounding value "y": If (y - (Evaluate("Mod(" &amp; x &amp; "," &amp; y &amp; ")"))) &lt;= (Evaluate("Mod(" &amp; x &amp; "," &amp; y &amp; ")")) Then Ans = x + (y - (Evaluate("Mod(" &amp; x &amp; "," &amp; y &amp; ")"))) Else Ans = x - (Evaluate("Mod(" &amp; x &amp; "," &amp; y &amp; ")")) End If ROUND = Ans End Function
    • 这里的答案很好。我将这里的功能组合在一起并想出了:round_well &lt;- function(x, roundTo, dir = NULL) { if(is.null(dir)){ if((roundTo - x %% roundTo) &lt;= x %% roundTo) { x + (roundTo - x %% roundTo)} else { x - (x %% roundTo)}} else if(dir == "Up") { ##ROUND UP x + (roundTo - x %% roundTo) } else if (dir == "Down") { ##ROUND DOWN x - (x %% roundTo) } else {print("Options are Up, Down, and blank.")} } 默认为正常的舍入规则到任何要选择的数字,但你可以通过“向上”或“向下”来指定方向
    【解决方案7】:

    关于四舍五入到任意数的重数,例如10,这是詹姆斯答案的简单替代方案。

    它适用于四舍五入的任何实数数 (from) 和四舍五入的任何正数数 (to):

    > RoundUp <- function(from,to) ceiling(from/to)*to
    

    例子:

    > RoundUp(-11,10)
    [1] -10
    > RoundUp(-0.1,10)
    [1] 0
    > RoundUp(0,10)
    [1] 0
    > RoundUp(8.9,10)
    [1] 10
    > RoundUp(135,10)
    [1] 140
    
    > RoundUp(from=c(1.3,2.4,5.6),to=1.1)  
    [1] 2.2 3.3 6.6
    

    【讨论】:

      【解决方案8】:

      如果你总是想将一个数字向上四舍五入到最接近的 X,你可以使用ceiling 函数:

      #Round 354 up to the nearest 100:
      > X=100
      > ceiling(354/X)*X
      [1] 400
      
      #Round 47 up to the nearest 30:
      > Y=30
      > ceiling(47/Y)*Y
      [1] 60
      

      同样,如果您总是想向下舍入,请使用floor 函数。如果您想简单地向上或向下舍入到最接近的 Z,请改用 round

      > Z=5
      > round(367.8/Z)*Z
      [1] 370
      > round(367.2/Z)*Z
      [1] 365
      

      【讨论】:

        【解决方案9】:

        我认为您的代码只需稍作修改就可以很好地工作:

        foo <- function(x, round=10) ceiling(max(x+10^-9)/round + 1/round)*round
        

        你的例子运行:

        > foo(4, round=1) == 5
        [1] TRUE
        > foo(6.1) == 10            #maybe 7 would be better
        [1] TRUE
        > foo(6.1, round=1) == 7    # you got 7
        [1] TRUE
        > foo(30.1) == 40
        [1] TRUE
        > foo(100.1) == 110
        [1] TRUE
        > # ALL in one:
        > foo(c(4, 6.1, 30.1, 100))
        [1] 110
        > foo(c(4, 6.1, 30.1, 100), round=10)
        [1] 110
        > foo(c(4, 6.1, 30.1, 100), round=2.3)
        [1] 101.2
        

        我通过两种方式改变了你的功能:

        • 添加了第二个参数(为您指定的X
        • 如果您想要更大的数字,请在max(x) 中添加一个小值(=1e-09,请随时修改!)

        【讨论】:

          【解决方案10】:

          你会发现Tommy's answer的升级版考虑了几种情况:

          • 在下限或上限之间进行选择
          • 考虑负值和零值
          • 两个不同的漂亮刻度,以防您希望函数舍入不同的大小数字。示例:4 将四舍五入为 0,而 400 将四舍五入为 400。

          代码下方:

          round.up.nice <- function(x, lower_bound = TRUE, nice_small=c(0,5,10), nice_big=c(1,2,3,4,5,6,7,8,9,10)) {
            if (abs(x) > 100) {
              nice = nice_big
            } else {
              nice = nice_small
            }
            if (lower_bound == TRUE) {
              if (x > 0) {
                return(10^floor(log10(x)) * nice[[max(which(x >= 10^floor(log10(x)) * nice))[[1]]]])
              } else if (x < 0) {
                return(- 10^floor(log10(-x)) * nice[[min(which(-x <= 10^floor(log10(-x)) * nice))[[1]]]])
              } else {
                return(0)
              }
            } else {
              if (x > 0) {
                return(10^floor(log10(x)) * nice[[min(which(x <= 10^floor(log10(x)) * nice))[[1]]]])
              } else if (x < 0) {
                return(- 10^floor(log10(-x)) * nice[[max(which(-x >= 10^floor(log10(-x)) * nice))[[1]]]])
              } else {
                return(0)
              }
            }
          }
          

          【讨论】:

          • 默认输出是向下取整:&gt; round.up.nice(.01) [1] 0 &gt; round.up.nice(4.5) [1] 0 &gt; round.up.nice(56) [1] 50
          • 我认为部分问题在于 nice_bignice_small 是向后定义的(如果我们在函数中翻转它们,round.up.nice(4.5) 变为 4)但它仍然向下舍入。
          【解决方案11】:

          我在没有使用任何外部库或神秘功能的情况下进行了尝试,它确实有效!

          希望对某人有所帮助。

          ceil <- function(val, multiple){
            div = val/multiple
            int_div = as.integer(div)
            return (int_div * multiple + ceiling(div - int_div) * multiple)
          }
          
          > ceil(2.1, 2.2)
          [1] 2.2
          > ceil(3, 2.2)
          [1] 4.4
          > ceil(5, 10)
          [1] 10
          > ceil(0, 10)
          [1] 0
          

          【讨论】:

            【解决方案12】:

            当第二个参数为正时,此函数将指定为第一个参数的数字向上舍入到指定为第二个参数的数字的最接近整数倍,而当第二个参数为负时,则向下舍入:

            rom=function(x,y)x+(y-x%%y)%%y
            rom(8.69,.1) # 8.7
            rom(8.69,-.1) # 8.6
            rom(8.69,.25) # 8.75
            rom(8.69,-.25) # 8.5
            rom(-8.69,.25) # -8.5
            

            这会四舍五入到最接近的倍数,例如 round_any in plyr (https://github.com/hadley/plyr/blob/34188a04f0e33c4115304cbcf40e5b1c7b85fedf/R/round-any.r):

            rnm=function(x,y)round(x/y)*y
            rnm(8.69,.25) # 8.75
            plyr::round_any(8.69,.25) # 8.75
            

            round_any 也可以作为第三个参数给出 ceiling 以始终向上舍入,或 floor 始终向下舍入:

            plyr::round_any(8.51,.25,ceiling) # 8.75
            plyr::round_any(8.69,.25,floor) # 8.5
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-09-24
              • 1970-01-01
              • 2012-01-29
              • 1970-01-01
              • 2017-07-26
              • 2011-03-21
              • 1970-01-01
              相关资源
              最近更新 更多