Compter le nombre d'éléments non-NaN dans un ndarray numpy en Python


87

J'ai besoin de calculer le nombre d'éléments non-NaN dans une matrice ndarray numpy. Comment faire cela efficacement en Python? Voici mon code simple pour y parvenir:

import numpy as np

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

Existe-t-il une fonction intégrée pour cela dans numpy? L'efficacité est importante car je fais des analyses Big Data.

Thnx pour toute aide!


2
Cette question semble être hors sujet car elle appartient à codereview.stackexchange.com
jonrsharpe

1
Vous voulez dire efficace en termes de mémoire?
Ashwini Chaudhary

+1 Je pensais au temps CPU, mais oui pourquoi pas la mémoire aussi. Le plus rapide et le moins cher, mieux c'est =)
jjepsuomi

3
@jjepsuomi Une version efficace de la mémoire sera sum(not np.isnan(x) for x in a), mais en termes de vitesse, elle est lente par rapport à la version numpy @ M4rtini.
Ashwini Chaudhary

@AshwiniChaudhary Merci beaucoup! J'ai besoin de voir lequel est le plus important dans mon application =)
jjepsuomi

Réponses:


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

~inverse la matrice booléenne renvoyée par np.isnan.

np.count_nonzerocompte les valeurs qui ne sont pas 0 \ false. .sumdevrait donner le même résultat. Mais peut-être plus clairement à utilisercount_nonzero

Vitesse de test:

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))semble à peine être le plus rapide ici. d'autres données peuvent donner des résultats de vitesse relative différents.


+1 @ M4rtini merci encore! Tu es genial! ; DI acceptera votre réponse dès que possible :)
jjepsuomi

3
Peut-être même numpy.isnan(array).sum()? Je ne suis pas très compétent avec numpy cependant.
msvalkon le

2
@msvalkon, il comptera le nombre de NaN, tandis que OP veut le nombre d'éléments non-NaN.
falsetru


5
Une extension de la réponse @msvalkon: data.size - np.isnan(data).sum()sera légèrement plus efficace.
Daniel

10

Alternative rapide à écrire

Même si ce n'est pas le choix le plus rapide, si les performances ne sont pas un problème, vous pouvez utiliser:

sum(~np.isnan(data)).

Performance:

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

Cette réponse fournit la somme qui n'est pas la même que le comptage du nombre d'éléments ... Vous devriez utiliser à la lenplace.
BenT

@BenT la somme des éléments d'un tableau booléen qui remplissent une certaine condition est la même en fournissant le len d'un tableau de sous-ensemble avec les éléments qui remplissent une certaine condition. Pouvez-vous s'il vous plaît préciser où cela ne va pas?
GM

1
Mon erreur j'ai oublié un retour booléen.
BenT

3

Pour déterminer si le tableau est clairsemé, il peut être utile d'obtenir une proportion de valeurs nan

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

Si cette proportion dépasse un seuil, utilisez un tableau fragmenté, par exemple - https://sparse.pydata.org/en/latest/


2

Une alternative, mais un peu plus lente, consiste à le faire sur l'indexation.

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 

La double utilisation de np.isnan(data)et de l' ==opérateur peut être un peu exagérée et j'ai donc posté la réponse uniquement par souci d'exhaustivité.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.