在numpy中将负数舍入为零时,如何消除多余的负号?

2024-06-26 02:20:49 发布

您现在位置:Python中文网/ 问答频道 /正文

关于numpy中的fixfloor函数,我有一个简单的问题。 当将大于-1的负数舍入为零时,numpy将它们正确舍入为零,但是会留下负号。这个负号干扰了我的服装独特行函数,因为它使用^{}来比较数组元素,而这个符号干扰了唯一性。在这方面,round和fix的行为相同。

>>> np.fix(-1e-6)
Out[1]: array(-0.0)
>>> np.round(-1e-6)
Out[2]: -0.0

你对如何去掉这个标志有什么看法吗?我考虑过使用np.sign函数,但它需要额外的计算成本。

提前谢谢。


Tags: 函数numpy元素标志np符号数组out
2条回答

我认为最根本的问题是你在浮点数上使用了类似集合的操作——这是作为一个一般规则应该避免的,除非你有很好的理由和对浮点数的深刻理解。

遵循这一规则的明显原因是,即使两个浮点寄存器之间的一个非常小的差异也是绝对差异,因此数值错误可能会导致类集合操作产生意外的结果。现在,在您的用例中,最初您似乎已经通过先舍入来避免了这个问题,从而限制了可能值的范围。但事实证明,意外的结果仍然是可能的,正如这个角落的案例所显示的那样。浮点数很难解释。

我认为正确的解决方法是使用astype将其舍入并然后转换为int

>>> a
array([-0.5,  2. ,  0.2, -3. , -0.2])
>>> numpy.fix(a)
array([-0.,  2.,  0., -3., -0.])
>>> numpy.fix(a).astype(int)    # could also use 'i8', etc...
array([ 0,  2,  0, -3,  0])

因为您已经进行了舍入,所以这不应该丢弃任何信息,而且对于以后的set-like操作,它将更加稳定和可预测。这是最好使用正确抽象的情况之一!

如果你需要浮动,你总是可以转换回来。唯一的问题是它创建了另一个副本;但大多数情况下这并不是一个真正的问题。numpy足够快,复制的开销非常小!

我要补充的是,如果你的案例确实需要使用float,那么tom10的答案是一个很好的答案。但我觉得,无论是浮动操作还是集合式操作都是真正必要的情况非常少。

-0.+0.之间存在的问题是浮动应该如何工作的规范(IEEE754)的一部分。在某些情况下,人们需要这种区别。例如,请参见在the docs for ^{}中链接到的文档。

值得注意的是,两个0应该比较为相等,所以

np.array(-0.)==np.array(+0.) 
# True

也就是说,我认为这个问题更可能与您的唯一性比较有关。例如:

a = np.array([-1., -0., 0., 1.])
np.unique(a)
#  array([-1., -0.,  1.])

如果要将数字保留为浮点,但所有零都相同,可以使用:

x = np.linspace(-2, 2, 6)
#  array([-2. , -1.2, -0.4,  0.4,  1.2,  2. ])
y = x.round()
#  array([-2., -1., -0.,  0.,  1.,  2.])
y[y==0.] = 0.
#  array([-2., -1.,  0.,  0.,  1.,  2.])

# or  
y += 0.
#  array([-2., -1.,  0.,  0.,  1.,  2.])    

不过,请注意,您确实需要做这一点额外的工作,因为您正试图避免使用浮点规范。

还要注意,这不是由于舍入误差造成的。例如

np.fix(np.array(-.4)).tostring().encode('hex')
# '0000000000000080'
np.fix(np.array(-0.)).tostring().encode('hex')
# '0000000000000080'

也就是说,得到的数字完全相同,但是

np.fix(np.array(0.)).tostring().encode('hex')
# '0000000000000000'

是不同的。这就是为什么你的方法不起作用的原因,因为它比较的是数字的二进制表示,这对两个零是不同的。因此,我认为问题更多的是比较的方法,而不是比较浮点数的唯一性的一般思想。

各种方法的快速计时测试:

data0 = np.fix(4*np.random.rand(1000000,)-2)
#   [ 1. -0.  1. -0. -0.  1.  1.  0. -0. -0. .... ]

N = 100
data = np.array(data0)
print timeit.timeit("data += 0.", setup="from __main__ import np, data", number=N)
#  0.171831846237
data = np.array(data0)
print timeit.timeit("data[data==0.] = 0.", setup="from __main__ import np, data", number=N)
#  0.83500289917
data = np.array(data0)
print timeit.timeit("data.astype(np.int).astype(np.float)", setup="from __main__ import np, data", number=N)
#  0.843791007996

我同意@senderle的观点,如果您想要简单而精确的比较,并且可以使用int,那么int通常会更容易。但如果你想要独特的浮动,你也应该能够做到这一点,虽然你需要做得更仔细一点。浮动的主要问题是,可以有一些小的差异,这些差异可以从计算中引入,并且不会出现在正常的print中,但这并不是一个巨大的障碍,尤其是对于合理范围的浮动,在round, fix, rint之后。

相关问题 更多 >