Contando el número de elementos que no son NaN en un ndarray numpy en Python

87

Necesito calcular la cantidad de elementos que no son NaN en una matriz ndarray numpy. ¿Cómo se podría hacer esto de manera eficiente en Python? Aquí está mi código simple para lograr esto:

import numpy as np

def numberOfNonNans(data):
    count = 0
    for i in data:
        if not np.isnan(i):
            count += 1
    return count 

¿Hay una función incorporada para esto en numpy? La eficiencia es importante porque estoy haciendo análisis de Big Data.

¡Gracias por cualquier ayuda!

jjepsuomi
fuente
2
Esta pregunta parece estar fuera de tema porque pertenece a codereview.stackexchange.com
jonrsharpe
1
¿Te refieres a eficiente en términos de memoria?
Ashwini Chaudhary
+1 Estaba pensando en el tiempo de la CPU, pero sí, ¿por qué no en la memoria también? Cuanto más rápido y más barato, mejor =)
jjepsuomi
3
@jjepsuomi Será una versión eficiente en memoria sum(not np.isnan(x) for x in a), pero en términos de velocidad es lenta en comparación con la versión numpy de @ M4rtini.
Ashwini Chaudhary
@AshwiniChaudhary ¡Muchas gracias! Necesito ver cuál es más importante en mi aplicación =)
jjepsuomi

Respuestas:

161
np.count_nonzero(~np.isnan(data))

~invierte la matriz booleana devuelta np.isnan.

np.count_nonzerocuenta valores que no es 0 \ falso. .sumdebería dar el mismo resultado. Pero tal vez sea más claro para usarcount_nonzero

Velocidad de prueba:

In [23]: data = np.random.random((10000,10000))

In [24]: data[[np.random.random_integers(0,10000, 100)],:][:, [np.random.random_integers(0,99, 100)]] = np.nan

In [25]: %timeit data.size - np.count_nonzero(np.isnan(data))
1 loops, best of 3: 309 ms per loop

In [26]: %timeit np.count_nonzero(~np.isnan(data))
1 loops, best of 3: 345 ms per loop

In [27]: %timeit data.size - np.isnan(data).sum()
1 loops, best of 3: 339 ms per loop

data.size - np.count_nonzero(np.isnan(data))parece apenas ser el más rápido aquí. otros datos pueden dar diferentes resultados de velocidad relativa.

M4rtini
fuente
+1 @ M4rtini ¡gracias de nuevo! ¡Eres genial! ; DI aceptará su respuesta tan pronto como pueda :)
jjepsuomi
3
¿Quizás incluso numpy.isnan(array).sum()? Sin embargo, no soy muy competente con numpy.
msvalkon
2
@msvalkon, contará el número de NaN, mientras que OP quiere el número de elementos no NaN.
falsetru
2
@goncalopp stackoverflow.com/questions/8305199/… =)
jjepsuomi
5
Una extensión de la respuesta de @msvalkon: data.size - np.isnan(data).sum()será un poco más eficiente.
Daniel
10

Alternativa de escritura rápida

Aunque no es la opción más rápida, si el rendimiento no es un problema, puede utilizar:

sum(~np.isnan(data)).

Actuación:

In [7]: %timeit data.size - np.count_nonzero(np.isnan(data))
10 loops, best of 3: 67.5 ms per loop

In [8]: %timeit sum(~np.isnan(data))
10 loops, best of 3: 154 ms per loop

In [9]: %timeit np.sum(~np.isnan(data))
10 loops, best of 3: 140 ms per loop
GM
fuente
Esta respuesta proporciona la suma que no es lo mismo que contar el número de elementos ... Debería usar lenen su lugar.
BenT
@BenT la suma de los elementos de una matriz bool que cumplen una determinada condición es la misma que proporciona la longitud de una matriz de subconjuntos con los elementos que cumplen una determinada condición. ¿Puede aclarar dónde está mal?
GM
1
Mi error olvidé un booleano obtenido.
BenT
3

Para determinar si la matriz es escasa, puede ser útil obtener una proporción de valores nan

np.isnan(ndarr).sum() / ndarr.size

Si esa proporción supera un umbral, utilice una matriz dispersa, por ejemplo: https://sparse.pydata.org/en/latest/

Darren Weber
fuente
2

Una alternativa, pero un poco más lenta, es hacerlo sobre indexación.

np.isnan(data)[np.isnan(data) == False].size

In [30]: %timeit np.isnan(data)[np.isnan(data) == False].size
1 loops, best of 3: 498 ms per loop 

El doble uso de np.isnan(data)y el ==operador podría ser un poco exagerado, por lo que publiqué la respuesta solo para completar.

Manuel
fuente