21

是否有一种惯用的方法来比较两个将 NaN 视为彼此相等的 NumPy 数组(但不等于 NaN 以外的任何东西

例如,我希望以下两个数组比较相等:

np.array([1.0, np.NAN, 2.0])
np.array([1.0, np.NAN, 2.0])

和以下两个数组比较不相等:

np.array([1.0, np.NAN, 2.0])
np.array([1.0, 0.0, 2.0])

我正在寻找一种可以产生标量布尔结果的方法。

以下将做到这一点:

np.all((a == b) | (np.isnan(a) & np.isnan(b)))

但它很笨重并且会创建所有这些中间数组。

有没有一种方法可以让眼睛更轻松并更好地利用内存?

PS如果有帮助,已知数组具有相同的形状和dtype。

4

4 回答 4

17

如果您真的关心内存使用(例如,有非常大的数组),那么您应该使用 numexpr 并且以下表达式将适用于您:

np.all(numexpr.evaluate('(a==b)|((a!=a)&(b!=b))'))

我已经在长度为 3e8 的非常大的数组上对其进行了测试,并且代码在我的机器上的性能与

np.all(a==b)

并使用相同数量的内存

于 2012-05-30T17:29:18.967 回答
9

Numpy 1.10 将equal_nan关键字添加到np.allclose( https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html )。

所以你现在可以这样做:

In [24]: np.allclose(np.array([1.0, np.NAN, 2.0]), 
                     np.array([1.0, np.NAN, 2.0]), equal_nan=True)
Out[24]: True
于 2016-11-02T16:37:25.933 回答
8

免责声明:我不建议经常使用它,我自己也不会使用它,但我可以想象在极少数情况下它可能有用。

如果数组具有相同的形状和 dtype,您可以考虑使用低级memoryview

>>> import numpy as np
>>> 
>>> a0 = np.array([1.0, np.NAN, 2.0])
>>> ac = a0 * (1+0j)
>>> b0 = np.array([1.0, np.NAN, 2.0])
>>> b1 = np.array([1.0, np.NAN, 2.0, np.NAN])
>>> c0 = np.array([1.0, 0.0, 2.0])
>>> 
>>> memoryview(a0)
<memory at 0x85ba1bc>
>>> memoryview(a0) == memoryview(a0)
True
>>> memoryview(a0) == memoryview(ac) # equal but different dtype
False
>>> memoryview(a0) == memoryview(b0) # hooray!
True
>>> memoryview(a0) == memoryview(b1)
False
>>> memoryview(a0) == memoryview(c0)
False

但要小心像这样的微妙问题:

>>> zp = np.array([0.0])
>>> zm = -1*zp
>>> zp
array([ 0.])
>>> zm
array([-0.])
>>> zp == zm
array([ True], dtype=bool)
>>> memoryview(zp) == memoryview(zm)
False

发生这种情况是因为即使它们比较相等,二进制表示也不同(当然,它们必须:这就是它知道打印负号的方式)

>>> memoryview(zp)[0]
'\x00\x00\x00\x00\x00\x00\x00\x00'
>>> memoryview(zm)[0]
'\x00\x00\x00\x00\x00\x00\x00\x80'

从好的方面来说,它会以您希望的方式短路:

In [47]: a0 = np.arange(10**7)*1.0
In [48]: a0[-1] = np.NAN    
In [49]: b0 = np.arange(10**7)*1.0    
In [50]: b0[-1] = np.NAN     
In [51]: timeit memoryview(a0) == memoryview(b0)
10 loops, best of 3: 31.7 ms per loop
In [52]: c0 = np.arange(10**7)*1.0    
In [53]: c0[0] = np.NAN   
In [54]: d0 = np.arange(10**7)*1.0    
In [55]: d0[0] = 0.0    
In [56]: timeit memoryview(c0) == memoryview(d0)
100000 loops, best of 3: 2.51 us per loop

为了比较:

In [57]: timeit np.all((a0 == b0) | (np.isnan(a0) & np.isnan(b0)))
1 loops, best of 3: 296 ms per loop
In [58]: timeit np.all((c0 == d0) | (np.isnan(c0) & np.isnan(d0)))
1 loops, best of 3: 284 ms per loop
于 2012-05-30T17:18:02.927 回答
0

不确定这是否更好,但一个想法......

import numpy
class FloatOrNaN(numpy.float_):
    def __eq__(self, other):
        return (numpy.isnan(self) and numpy.isnan(other)) or super(FloatOrNaN,self).__eq__(other)

a = [1., np.nan, 2.]
one = numpy.array([FloatOrNaN(val) for val in a], dtype=object)
two = numpy.array([FloatOrNaN(val) for val in a], dtype=object)
print one == two   # yields  array([ True,  True,  True], dtype=bool)

这将丑陋推入 dtype,以牺牲内部工作 python 而不是 c 为代价(Cython/etc 将解决此问题)。但是,它确实大大降低了内存成本。

不过还是有点丑:(

于 2012-05-30T16:42:44.097 回答