Je cherche le moyen le plus rapide de vérifier l'occurrence de NaN (np.nan
) Dans un tableau NumPy X
. np.isnan(X)
est hors de question, car il construit un tableau booléen de forme X.shape
, ce qui est potentiellement gigantesque.
J'ai essayé np.nan in X
, Mais cela ne semble pas fonctionner car np.nan != np.nan
. Existe-t-il un moyen rapide et efficace en termes de mémoire?
(A ceux qui voudraient savoir "à quel point gigantesque": je ne peux pas dire. C'est la validation d'entrée pour le code de bibliothèque.)
La solution de Ray est bonne. Cependant, sur ma machine, il est environ 2,5 fois plus rapide à utiliser numpy.sum
à la place 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
Contrairement à min
, sum
ne nécessite pas de création de branches, ce qui sur le matériel moderne a tendance à être assez coûteux. C'est probablement la raison pour laquelle sum
est plus rapide.
edit Le test ci-dessus a été effectué avec un seul NaN au milieu du tableau.
Il est intéressant de noter que min
est plus lent en présence de NaN qu'en leur absence. Cela semble également devenir plus lent à mesure que les NaN se rapprochent du début de la matrice. D'autre part, le débit de sum
semble constant, qu'il y ait ou non des NaN et où ils se trouvent:
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
Je pense que np.isnan(np.min(X))
devrait faire ce que vous voulez.
Même s'il existe une réponse acceptée, j'aimerai démontrer ce qui suit (avec Python 2.7.2 et Numpy 1.6.0 sur 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
Ainsi, le moyen le plus efficace peut être fortement dépendant du système d’exploitation. Quoi qu'il en soit, la fonction dot(.)
semble être la plus stable.
Il y a deux approches générales ici:
nan
et prenez any
.nan
s (comme sum
) et vérifiez son résultat.Bien que la première approche soit certainement la plus propre, l’optimisation lourde de certaines opérations cumulatives (en particulier celles exécutées dans BLAS, comme dot
) peut les rendre assez rapides. Notez que dot
, comme certaines autres opérations BLAS, est multithread sous certaines conditions. Ceci explique la différence de vitesse entre différentes machines.
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)",
)
utilisez .any ()
if numpy.isnan(myarray).any()
numpy.isfinite peut-être mieux qu'isnan pour vérifier
if not np.isfinite(prop).all()
Si vous êtes à l'aise avec numba , cela permet de créer un court-circuit rapide (qui s'arrête dès qu'un NaN est détecté):
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
S'il n'y a pas NaN
, la fonction pourrait en réalité être plus lente que np.min
, Je pense que c'est parce que np.min
utilise le multitraitement pour les tableaux de grande taille:
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
Mais au cas où il y aurait un NaN dans le tableau, surtout si sa position est à des indices bas, c'est beaucoup plus rapide:
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
Des résultats similaires peuvent être obtenus avec Cython ou une extension C, ils sont un peu plus compliqués (ou facilement disponibles en tant que bottleneck.anynan
) mais fait finalement la même chose que ma fonction anynan
.
Liée à ceci est la question de savoir comment trouver la première occurrence de NaN. C'est le moyen le plus rapide de gérer cela que je sache:
index = next((i for (i,n) in enumerate(iterable) if n!=n), None)