在numpy中按最近步长取整
我想知道如何在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 个回答
请注意,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
总结:这是正确的做法,最上面的回答有些情况是不对的:
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库,链接在这里
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)
。