change - nan python
Verificación rápida de NaN en NumPy (7)
Estoy buscando la manera más rápida de verificar la ocurrencia de NaN ( np.nan
) en una matriz NumPy X
np.isnan(X)
está fuera de discusión, ya que construye una matriz booleana de forma X.shape
, que es potencialmente gigantesca.
np.nan in X
, pero parece que no funciona porque np.nan != np.nan
. ¿Hay alguna forma rápida y eficiente de memoria para hacer esto?
(Para aquellos que preguntan "qué tan gigantesco": no puedo decir. Esta es la validación de entrada para el código de la biblioteca).
Aquí hay dos enfoques generales:
- Compruebe cada elemento de la matriz para
nan
si haynan
y tomeany
. - Aplique una operación acumulativa que preserve
nan
(como lasum
) y verifique su resultado.
Si bien el primer enfoque es sin duda el más limpio, la gran optimización de algunas de las operaciones acumulativas (particularmente las que se ejecutan en BLAS, como el dot
) puede hacerlas bastante rápidas. Tenga en cuenta que el dot
, al igual que otras operaciones BLAS, tiene múltiples subprocesos bajo ciertas condiciones. Esto explica la diferencia de velocidad entre diferentes máquinas.
import numpy
import perfplot
def min(a):
return numpy.isnan(numpy.min(a))
def sum(a):
return numpy.isnan(numpy.sum(a))
def dot(a):
return numpy.isnan(numpy.dot(a, a))
def any(a):
return numpy.any(numpy.isnan(a))
def einsum(a):
return numpy.isnan(numpy.einsum(''i->'', a))
perfplot.show(
setup=lambda n: numpy.random.rand(n),
kernels=[min, sum, dot, any, einsum],
n_range=[2**k for k in range(20)],
logx=True,
logy=True,
xlabel=''len(a)''
)
Creo que np.isnan(np.min(X))
debería hacer lo que quieras.
Incluso si existe una respuesta aceptada, me gustaría demostrar lo siguiente (con Python 2.7.2 y Numpy 1.6.0 en Vista):
In []: x= rand(1e5)
In []: %timeit isnan(x.min())
10000 loops, best of 3: 200 us per loop
In []: %timeit isnan(x.sum())
10000 loops, best of 3: 169 us per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 134 us per loop
In []: x[5e4]= NaN
In []: %timeit isnan(x.min())
100 loops, best of 3: 4.47 ms per loop
In []: %timeit isnan(x.sum())
100 loops, best of 3: 6.44 ms per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 138 us per loop
Por lo tanto, la forma realmente eficiente podría depender en gran medida del sistema operativo. De todos modos, la base de dot(.)
Parece ser la más estable.
La solución de Ray es buena. Sin embargo, en mi máquina es aproximadamente 2,5 numpy.sum
más rápido usar numpy.sum
en lugar de numpy.min
:
In [13]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 244 us per loop
In [14]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 97.3 us per loop
A diferencia de min
, sum
no requiere ramificación, lo que en hardware moderno tiende a ser bastante caro. Esta es probablemente la razón por la sum
es más rápido.
editar La prueba anterior se realizó con un único NaN justo en el medio de la matriz.
Es interesante observar que min
es más lento en presencia de NaN que en su ausencia. También parece hacerse más lento a medida que los NaN se acercan al inicio de la matriz. Por otro lado, el rendimiento de la sum
parece constante independientemente de si hay NaN y dónde se encuentran:
In [40]: x = np.random.rand(100000)
In [41]: %timeit np.isnan(np.min(x))
10000 loops, best of 3: 153 us per loop
In [42]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop
In [43]: x[50000] = np.nan
In [44]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 239 us per loop
In [45]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.8 us per loop
In [46]: x[0] = np.nan
In [47]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 326 us per loop
In [48]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop
Relacionado con esto está la cuestión de cómo encontrar la primera aparición de NaN. Esta es la manera más rápida de manejar lo que yo sé:
index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
Si te sientes cómodo con numba , numba crear una función rápida de cortocircuito (se detiene tan pronto como se encuentre un NaN):
import numba as nb
import math
@nb.njit
def anynan(array):
array = array.ravel()
for i in range(array.size):
if math.isnan(array[i]):
return True
return False
Si no hay NaN
la función podría ser más lenta que np.min
, creo que es porque np.min
utiliza el multiprocesamiento para matrices grandes:
import numpy as np
array = np.random.random(2000000)
%timeit anynan(array) # 100 loops, best of 3: 2.21 ms per loop
%timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.45 ms per loop
%timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.64 ms per loop
Pero en caso de que haya un NaN en la matriz, especialmente si su posición está en índices bajos, entonces es mucho más rápido:
array = np.random.random(2000000)
array[100] = np.nan
%timeit anynan(array) # 1000000 loops, best of 3: 1.93 µs per loop
%timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.57 ms per loop
%timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.65 ms per loop
Se pueden lograr resultados similares con Cython o una extensión C, estos son un poco más complicados (o fácilmente disponibles como bottleneck.anynan
) pero, por último, hacen lo mismo que mi función anynan
.
enter code here
- usar cualquier()
if numpy.isnan(myarray).any()
- numpy.isfinite tal vez mejor que isnan para verificar
if not np.isfinite(prop).all()