在numpy中按最近步长取整

10 投票
3 回答
15120 浏览
提问于 2025-04-17 04:48

我想知道如何在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)

3 个回答

2

请注意,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)

这样会得到:

test1 = 50
test2 = 150 
8

总结:这是正确的做法,最上面的回答有些情况是不对的:

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库,链接在这里

20

pb360 提出的解决方案要好得多,它使用了 Python 3 中内置的 round 函数的第二个参数。

我觉得你不需要用到 numpy

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

这里 a 是一个单独的数字,如果你想让这个函数支持多个数字,你只需要把 round 替换成 np.round,把 float(a) 替换成 np.array(a, dtype=float)

撰写回答