Сравнивая массивы NumPy так, чтобы NaN сравнивали равные

Существует ли идиоматический способ сравнения двух массивов NumPy, которые будут рассматривать NaN как равные друг другу (но не равные чему-либо, кроме 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 Если это помогает, массивы, как известно, имеют одинаковую форму и тип.

Если вы действительно заботитесь об использовании памяти (например, имеете очень большие массивы), то вы должны использовать numexpr, и следующее выражение будет работать для вас:

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

Я тестировал его на очень больших массивах с длиной 3e8, и код имеет такую ​​же производительность на моей машине, как и

 np.all(a==b) 

и использует тот же объем памяти

Отказ от ответственности: я не рекомендую это для регулярного использования, и я бы не использовал его сам, но я мог представить себе редкие обстоятельства, при которых это может быть полезно.

Если массивы имеют одинаковую форму и тип 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 

Numpy 1.10 добавила ключевое слово np.allclose в 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 

Не уверен, что это лучше, но мысль …

 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 исправит это). Тем не менее, это значительно снижает затраты на память.

Еще вроде уродливый, хотя 🙁