numpy中的四舍五入到最近步长

10
我想知道如何在numpy中将数字四舍五入到一个预定义的步长的上限或下限。更清晰地陈述,例如,如果我有数字123和步长等于50,则需要将123四舍五入到最接近150或100中的一个,此时为100。我编写了以下函数来实现此操作,但我想知道是否有更好、更简洁的方法。
提前感谢, Paolo
def getRoundedThresholdv1(a, MinClip):
    import numpy as np
    import math
    digits = int(math.log10(MinClip))+1
    b = np.round(a, -digits)
    if b > a:  # rounded-up
        c = b - MinClip
        UpLow = np.array((b,c))
    else:  # rounded-down
        c = b + MinClip
        UpLow = np.array((c,b))
    AbsDelta = np.abs(a - UpLow)
    return UpLow[AbsDelta.argmin()]




getRoundedThresholdv1(143, 50)

你的代码不起作用,例如getRoundedThresholdv1(143, 50)返回50而不是150。 - Ruggero Turra
现在它可以工作了 - 需要 int(math.log10(MinClip))+1 而不是 int(math.log10(a))+1 - 感谢您指出这一点。 - RandomCPlusPlus
3个回答

20

pb360的解决方案更好,使用Python3中内置round的第二个参数。

我认为你不需要使用numpy

def getRoundedThresholdv1(a, MinClip):
    return round(float(a) / MinClip) * MinClip

这里的a是一个单独的数字,如果你想对这个函数进行向量化,你只需要用np.round代替round,并用np.array(a, dtype=float)代替float(a)


3
好的回答:当然,如果您非常关心中间值情况被四舍五入的方式,那么生活会变得*更加困难。 - Mark Dickinson
1
@MarkDickinson:现在它会四舍五入到最近的数字,例如getRoundedThresholdv1(150, 100): 四舍五入到200,因为150比100更接近200,而0.5比0更接近1(round代数)。如果你想改变逻辑,可以尝试使用ceilfloor - Ruggero Turra
对于一个简单的例子,返回“true”。但是,如果您尝试使用此功能将数字四舍五入到0.1的倍数(例如),则会遇到数字误差问题,会导致计算结果微小地偏离真实结果,因此半路情况的输出变得不可预测。 - Mark Dickinson
@MarkDickinson: 天啊!“Numpy 四舍五入到最近的偶数值” - Ruggero Turra
警告:这个解决方案在我运行getRoundedThresholdv1(a=13.200000000000001, MinClip=0.0001)时出现了问题,返回值为13.200000000000001。 - pb360
显示剩余2条评论

7

总结:这是正确的做法,顶部答案有不起作用的情况:

def round_step_size(quantity: Union[float, Decimal], step_size: Union[float, Decimal]) -> float:
    """Rounds a given quantity to a specific step size
    :param quantity: required
    :param step_size: required
    :return: decimal
    """
    precision: int = int(round(-math.log(step_size, 10), 0))
    return float(round(quantity, precision))

我的声誉太低,无法在Ruggero Turra的顶级答案中发表评论并指出问题。然而,它有一些案例没有起作用,例如:

def getRoundedThresholdv1(a, MinClip):
    return round(float(a) / MinClip) * MinClip

getRoundedThresholdv1(quantity=13.200000000000001, step_size=0.0001)

无论是使用numpy还是标准库round,都会返回13.200000000000001。我甚至没有通过压力测试函数来发现这一点。它只是在生产代码中使用时出现了错误。请注意,本答案的全部信用来自于一个开源的github仓库,不是我的,可以在这里找到。

1
请注意,Ruggero Turra的回答中的round()会将数字四舍五入到最接近的偶数。也就是说:
a= 0.5
round(a)

Out: 0

这可能不是您期望的。
如果您需要“传统”的四舍五入,可以使用此函数,它支持标量和Numpy数组:
import Numpy as np

def getRoundedThresholdv1(a, MinClip):
    scaled = a/MinClip
    return np.where(scaled % 1 >= 0.5, np.ceil(scaled), np.floor(scaled))*MinClip

或者,您可以使用Numpy的digitize方法。它要求您定义步骤数组。digitize会将您的值“向上取整”到下一个步骤。因此,为了以“经典”方式四舍五入,我们需要一个中间步骤。

您可以使用以下内容:

import Numpy as np
    
def getRoundedThresholdv1(a, MinClipBins):
    intermediate = (MinClipBins[1:] + MinClipBins[:-1])/2
    return MinClipBins[np.discritize(a, intermediate)]

您可以这样调用它:

bins = np.array([0,  50, 100, 150])
test1 = getRoundedThresholdv1(74, bins)
test2 = getRoundedThresholdv1(125, bins)

Which gives:

test1 = 50
test2 = 150 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接