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

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

но он неуклюжий и создает все эти промежуточные массивы.

Есть ли способ, который облегчает глаза и лучше использует память?

Постскриптум Если это помогает, известно, что массивы имеют одинаковую форму и тип d.

 DSM30 мая 2012 г., 18:30
Если бы это не было для чисел, которые сравниваются равными, но имеют разные двоичные представления (0.0 и -0.0, например), то memoryview (a0) == memoryview (a1) сделает это ..
 NPE30 мая 2012 г., 18:38
@DSM: Спасибо за это. Это может действительно соответствовать требованиям для моего варианта использования. Не могли бы вы написать это как ответ?
 Joe Kington30 мая 2012 г., 18:10
Если вы используете текущую подсказку git для numpy, естьnumpy.isclose function это занимаетequal_nan ключевое слово аргумент (по умолчаниюFalse для совместимости). Это не очень благоприятно для памяти, хотя.
 NPE30 мая 2012 г., 17:51
@DanielRoseman: я это понимаю. У меня есть два способа создания массива NumPy, и мне нужно знать, производили ли они идентичные массивы.
 senderle30 мая 2012 г., 18:01
Вы исключили один ответ отthis question; ты исключаешь и двух других?

Ответы на вопрос(4)

Решение Вопроса

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

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

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

np.all(a==b)

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

 NPE30 мая 2012 г., 19:30
(+1) Хорошая идея, спасибо.

Disclaimer: I don't recommend this for regular use, and I wouldn't use it myself, but I could imagine rare circumstances under which it might be useful.

Если массивы имеют одинаковую форму и тип d, вы можете рассмотреть возможность использования низкоуровневых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
 NPE30 мая 2012 г., 19:19
(+1) Это здорово, спасибо, что нашли время написать это.
 30 мая 2012 г., 19:27
@aix: Мне действительно нужно было что-то похожее в прошлом (равное рассмотрение-nans-равное), хотя проблем с производительностью и памятью не было, поэтому я сделал это вручную. Возможно, стоит сделать запрос на функцию.

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
 19 дек. 2018 г., 17:49
Кстати, это не работает со строками. Сравнивая массивы со строками, получим:TypeError("ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''")

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

Все еще немного некрасиво, хотя :(

Ваш ответ на вопрос