169 votes

Détecter si un tableau NumPy contient au moins une valeur non numérique ?

Je dois écrire une fonction qui détectera si l'entrée contient au moins une valeur non numérique. Si une valeur non numérique est trouvée, je lèverai une erreur (parce que le calcul ne doit renvoyer qu'une valeur numérique). Le nombre de dimensions du tableau d'entrée n'est pas connu à l'avance - la fonction devrait donner la valeur correcte indépendamment de ndim. Pour compliquer encore les choses, l'entrée pourrait être un simple flotteur ou un tableau de numpy.float64 ou même quelque chose de bizarre comme un tableau à zéro dimension.

La façon la plus évidente de résoudre ce problème est d'écrire une fonction récursive qui passe en revue tous les objets itérables du tableau jusqu'à ce qu'elle trouve un objet non itérable. Elle appliquera la méthode numpy.isnan() sur chaque objet non itérable. Si au moins une valeur non numérique est trouvée, la fonction renvoie immédiatement False. Dans le cas contraire, si toutes les valeurs de l'objet itérable sont numériques, la fonction renverra finalement True.

Cela fonctionne très bien, mais c'est assez lent et je m'attends à ce que cela soit le cas. NumPy a une bien meilleure façon de procéder. Quelle est l'alternative la plus rapide et la plus proche de numpy ?

Voici ma maquette :

def contains_nan( myarray ):
    """
    @param myarray : An n-dimensional array or a single float
    @type myarray : numpy.ndarray, numpy.array, float
    @returns: bool
    Returns true if myarray is numeric or only contains numeric values.
    Returns false if at least one non-numeric value exists
    Not-A-Number is given by the numpy.isnan() function.
    """
    return True

291voto

Paul Points 13042

Cette méthode devrait être plus rapide que l'itération et fonctionnera quelle que soit la forme.

numpy.isnan(myarray).any()

Edit : 30x plus rapide :

import timeit
s = 'import numpy;a = numpy.arange(10000.).reshape((100,100));a[10,10]=numpy.nan'
ms = [
    'numpy.isnan(a).any()',
    'any(numpy.isnan(x) for x in a.flatten())']
for m in ms:
    print "  %.2f s" % timeit.Timer(m, s).timeit(1000), m

Résultats :

  0.11 s numpy.isnan(a).any()
  3.75 s any(numpy.isnan(x) for x in a.flatten())

Bonus : cela fonctionne parfaitement pour les types NumPy qui ne sont pas des tableaux :

>>> a = numpy.float64(42.)
>>> numpy.isnan(a).any()
False
>>> a = numpy.float64(numpy.nan)
>>> numpy.isnan(a).any()
True

30voto

Akavall Points 7357

Si l'infini est une valeur possible, j'utiliserais numpy.isfinite

numpy.isfinite(myarray).all()

Si l'évaluation ci-dessus est égale à True alors myarray ne contient aucun des éléments suivants numpy.nan , numpy.inf o -numpy.inf .

numpy.isnan sera d'accord avec numpy.inf par exemple :

In [11]: import numpy as np

In [12]: b = np.array([[4, np.inf],[np.nan, -np.inf]])

In [13]: np.isnan(b)
Out[13]: 
array([[False, False],
       [ True, False]], dtype=bool)

In [14]: np.isfinite(b)
Out[14]: 
array([[ True, False],
       [False, False]], dtype=bool)

14voto

user48956 Points 1708

Pfft ! Microsecondes ! Ne jamais résoudre en microsecondes un problème qui peut être résolu en nanosecondes.

Notez que la réponse acceptée :

  • itère sur l'ensemble des données, qu'un nan soit trouvé ou non
  • crée un tableau temporaire de taille N, ce qui est redondant.

Une meilleure solution consiste à renvoyer True immédiatement lorsque NAN est trouvé :

import numba
import numpy as np

NAN = float("nan")

@numba.njit(nogil=True)
def _any_nans(a):
    for x in a:
        if np.isnan(x): return True
    return False

@numba.jit
def any_nans(a):
    if not a.dtype.kind=='f': return False
    return _any_nans(a.flat)

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 573us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 774ns  (!nanoseconds)

et fonctionne pour n dimensions :

array1M_nd = array1M.reshape((len(array1M)/2, 2))
assert any_nans(array1M_nd)==True
%timeit any_nans(array1M_nd)  # 774ns

Comparez cela à la solution native de numpy :

def any_nans(a):
    if not a.dtype.kind=='f': return False
    return np.isnan(a).any()

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 456us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 470us

%timeit np.isnan(array1M).any()  # 532us

La méthode de sortie anticipée permet une accélération de 3 ordres de grandeur (dans certains cas). Ce n'est pas si mal pour une simple annotation.

3voto

Ting On Chan Points 71

(np.where(np.isnan(A)))[0].shape[0] sera supérieure à 0 si A contient au moins un élément de nan , A pourrait être un n x m matrice.

Exemple :

import numpy as np

A = np.array([1,2,4,np.nan])

if (np.where(np.isnan(A)))[0].shape[0]: 
    print "A contains nan"
else:
    print "A does not contain nan"

2voto

Avec numpy 1.3 ou svn, vous pouvez faire ceci

In [1]: a = arange(10000.).reshape(100,100)

In [3]: isnan(a.max())
Out[3]: False

In [4]: a[50,50] = nan

In [5]: isnan(a.max())
Out[5]: True

In [6]: timeit isnan(a.max())
10000 loops, best of 3: 66.3 µs per loop

Le traitement des nans dans les comparaisons n'était pas cohérent dans les versions précédentes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X