python - isnan - Comparando matrices NumPy para que los NaN se comparen iguales
nan python 3 (4)
No estoy seguro de que esto sea mejor, pero un pensamiento ...
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)
Esto empuja la fealdad al tipo de letra, a expensas de hacer que el funcionamiento interno sea python en lugar de c (Cython / etc solucionaría esto). Sin embargo, reduce considerablemente los costos de memoria.
Todavía un poco feo sin embargo :(
¿Existe una forma idiomática de comparar dos matrices NumPy que traten a los NaN como iguales entre sí (pero no igual a nada que no sea un NaN)?
Por ejemplo, quiero que las dos matrices siguientes se comparen igual:
np.array([1.0, np.NAN, 2.0])
np.array([1.0, np.NAN, 2.0])
y las siguientes dos matrices para comparar desiguales:
np.array([1.0, np.NAN, 2.0])
np.array([1.0, 0.0, 2.0])
Estoy buscando un método que produzca un resultado booleano escalar.
Lo siguiente lo haría:
np.all((a == b) | (np.isnan(a) & np.isnan(b)))
pero es torpe y crea todas esas matrices intermedias.
¿Hay alguna forma más fácil de usar y que haga un mejor uso de la memoria?
PD: Si ayuda, se sabe que las matrices tienen la misma forma y tipo.
Numpy 1.10 agregó la palabra clave np.allclose
a np.allclose
( https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html ).
Así que puedes hacer ahora:
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
Si realmente te importa el uso de la memoria (por ejemplo, tiene matrices muy grandes), entonces deberías usar numexpr y la siguiente expresión funcionará para ti:
np.all(numexpr.evaluate(''(a==b)|((a!=a)&(b!=b))''))
Lo he probado en arreglos muy grandes con una longitud de 3e8, y el código tiene el mismo rendimiento en mi máquina que
np.all(a==b)
y usa la misma cantidad de memoria
Descargo de responsabilidad: no lo recomiendo para uso regular, y no lo usaría yo mismo, pero podría imaginar circunstancias excepcionales en las que podría ser útil.
Si las matrices tienen la misma forma y tipo de dtype, podría considerar usar la vista de memoryview
bajo nivel:
>>> 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
Pero cuidado con problemas sutiles como este:
>>> 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
lo que sucede porque las representaciones binarias difieren aunque se comparan de la misma forma (tienen que hacerlo, por supuesto: así es como se sabe para imprimir el signo negativo)
>>> memoryview(zp)[0]
''/x00/x00/x00/x00/x00/x00/x00/x00''
>>> memoryview(zm)[0]
''/x00/x00/x00/x00/x00/x00/x00/x80''
En el lado positivo, se cortocircuita de la forma en que usted podría esperar:
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
y para comparación:
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