如何在numpy中将XX.5始终向上舍入

9
我了解到numpy在四舍五入时是无偏的,并且它按照设计工作。如果您总是将0.5向上舍入到下一个最大数字,则一堆舍入数字的平均值可能会略大于未舍入数字的平均值:这种偏差或漂移可能对某些数值算法产生非常不利的影响并使其不准确。
如果忽略此信息并假设我始终希望向上舍入,如何在numpy中实现呢?假设我的数组可能相当大。
为简单起见,假设我有以下数组:
import numpy as np

A = [ [10, 15, 30], [25, 134, 41], [134, 413, 51]]
A = np.array(A, dtype=np.int16)

decimal = A * .1
whole = np.round(decimal)

十进制数看起来像:

[[  1.    1.5   3. ]
 [  2.5  13.4   4.1]
 [ 13.4  41.3   5.1]]

整体看起来像这样:
[[  1.   2.   3.]
 [  2.  13.   4.]
 [ 13.  41.   5.]]

您可以看到,1.5被四舍五入为2,2.5也被四舍五入为2。如何强制始终获得一个四舍五入的答案?我知道我可以循环遍历数组并使用Python round()函数,但这肯定会慢得多。想知道是否有一种方法可以使用numpy函数来解决这个问题。


你希望负数的行为是什么?-2.5 应该舍入到 -3.0 还是 -2.0? - Mark Dickinson
这是一个很好的问题,尽管在我的情况下没有负数,所以我并没有考虑过它。 - user1179317
2个回答

3
答案几乎从不是np.vectorize。您可以并且应该以完全向量化的方式执行此操作。假设对于x >= 0,您希望r = floor(x + 0.5)。如果要使负数四舍五入为零,则相同的公式适用于x < 0。所以假设您总是想远离零进行四舍五入。在这种情况下,您要寻找ceil(x - 0.5)对于x < 0
要在不调用np.vectorize的情况下在整个数组上实现它,您可以使用遮罩:
def round_half_up(x):
    mask = (x >= 0)
    out = np.empty_like(x)
    out[mask] = np.floor(x[mask] + 0.5)
    out[~mask] = np.ceil(x[~mask] - 0.5)
    return out

请注意,如果您只朝一个方向四舍五入,则无需使用掩码:
def round_up(x):
    return np.floor(x + 0.5)

现在,如果您想使其更加高效,可以摆脱所有临时数组。这将充分利用ufuncs的全部功能:
def round_half_up(x):
    out = x.copy()
    mask = (out >= 0)
    np.add(out, 0.5, where=mask, out=out)
    np.floor(out, where=mask, out=out)
    np.invert(mask, out=mask)
    np.subtract(out, 0.5, where=mask, out=out)
    np.ceil(out, where=mask, out=out)
    return out

同时:

def round_up(x):
    out = x + 0.5
    np.floor(out, out=out)
    return out

1
import numpy as np
A = [ [1.0, 1.5, 3.0], [2.5, 13.4, 4.1], [13.4, 41.3, 5.1]]
A = np.array(A)

print(A)

def rounder(x):
    if (x-int(x) >= 0.5):
        return np.ceil(x)
    else:
        return np.floor(x)

rounder_vec = np.vectorize(rounder)
whole = rounder_vec(A)
print(whole)

或者,您还可以查看 numpy.ceil, numpy.floor, numpy.trunc 来了解其他舍入样式。


np.ceil总是向上舍入。我仍希望1.3四舍五入为1,1.5四舍五入为2。也许我的问题没有表达清楚,抱歉。 - user1179317
如果是这种情况,在numpy中就没有相应的函数。你可以通过循环遍历每个元素并检查四舍五入来解决问题。 - Andreas
1
你真的不应该在numpy中使用循环。 - Mad Physicist
你的函数需要在调用前设置一个临时 rounder_vec。这不是请求 - 请求应该是直接将输入传递到输出。 - GM1

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