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

Python 在numpy中将负数舍入到零时,如何消除多余的减号?,python,numpy,unique,rounding,Python,Numpy,Unique,Rounding,我有一个关于numpy中的fix和floor函数的简单问题。 将大于-1的负数向零舍入时,numpy将其正确舍入为零,但会留下负号。此负号会干扰my Court unique_rows函数,因为它使用来比较数组的元素,并且此符号会干扰唯一性。在这方面,round和fix的行为相同 >>> np.fix(-1e-6) Out[1]: array(-0.0) >>> np.round(-1e-6) Out[2]: -0.0 有没有关于如何摆脱这个标志的见解?我曾

我有一个关于
numpy
中的
fix
floor
函数的简单问题。 将大于-1的负数向零舍入时,
numpy
将其正确舍入为零,但会留下负号。此负号会干扰my Court unique_rows函数,因为它使用来比较数组的元素,并且此符号会干扰唯一性。在这方面,round和fix的行为相同

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

有没有关于如何摆脱这个标志的见解?我曾考虑过使用
np.sign
函数,但它会带来额外的计算成本。

我认为根本的问题是在浮点数上使用类似集合的运算——这是一个一般规则,除非你有很好的理由和对浮点数的深刻理解,否则要避免使用

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

我认为正确的解决方法是先取整,然后使用
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
速度足够快,因此复制的开销非常小


我要补充一点,如果你的案例真的需要使用浮动,那么我们的答案是好的。但是我觉得真正需要浮点数和类似集合的操作的情况很少。

-0.
+0之间存在的问题。
是浮点数行为规范的一部分(IEEE754)。在某些情况下,人们需要这种区别。例如,请参见中链接到的文档

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

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'
这是不同的。这就是为什么您的方法不起作用的原因,因为它比较的是数字的二进制表示,这两个零是不同的。因此,我认为问题更多的是比较方法,而不是比较浮点数的唯一性

各种方法的快速timeit测试:

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通常会更容易。但是,如果您想要独特的浮动,您也应该能够做到这一点,尽管您需要更加小心。浮点数的主要问题是,可以从计算中引入一些小的差异,这些差异不会出现在正常的
打印中,但这并不是一个巨大的障碍,尤其是在
轮、定、打印后,对于合理的浮点数范围,这不是一个障碍。

尝试将0.0添加到结果中。前面的问题是否相关?或添加0。输入:np.四舍五入(1e-6)+0。这只增加了很少的计算开销,从源头上解决了问题。我同意你的解决方案(so+1),但我认为原因是IEEE754标准规定
0.
-0.
不同(尽管它们应该比较相等)。@tom10,OP似乎意识到了这一点,你不这么认为吗?但这比你所说的还要复杂,因为我们特别讨论四舍五入。我不知道该标准在它定义的四个舍入规则中对有符号零规定了什么。想必
numpy
可以忽略这些规则,如果它愿意,只舍入到正零!我认为无论使用何种标准,这些问题都很难解决。我将删除我的评论,并写下我自己的答案。你在这里明确指出问题在于“数值误差”,我想说的是这不是问题所在。但我会在几分钟内删除这两条评论,以免弄乱局面。@tom10,我认为没有必要删除你的评论。我想我的答案不够清楚,但我并没有说问题出在数字错误上。我说过,问题是在类似集合的运算中使用浮点数——句点。我将重新措辞以澄清。我同意如果有必要坚持浮动,这是一个很好的方法。(我想知道它与Mark Ransom添加
0.0
的想法相比如何)此外,我认为正零和负零的出现是不同的,因为问题中链接的唯一性测试将数据转换为
np.void
。多亏了@Mark Ransom和@tom10。将0.0添加到
fix
round
命令的答案中,可以消除额外的负号,原因已在上面详述。解决了这个问题后,我能够为python编写一个函数来查找numpy数组中唯一的行,并可以选择接受精度(