web-dev-qa-db-fra.com

Vérification rapide de NaN dans NumPy

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.)

103
Fred Foo

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
142
NPE

Je pense que np.isnan(np.min(X)) devrait faire ce que vous voulez.

24
Ray

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.

17
eat

Il y a deux approches générales ici:

  • Vérifiez chaque élément du tableau pour nan et prenez any.
  • Appliquez une opération cumulative qui préserve nans (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.

enter image description here

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)",
)
9
Nico Schlömer
  1. utilisez .any ()

    if numpy.isnan(myarray).any()

  2. numpy.isfinite peut-être mieux qu'isnan pour vérifier

    if not np.isfinite(prop).all()

3
woso

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.

3
MSeifert

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)
1
vitiral