web-dev-qa-db-fra.com

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

J'ai besoin d'é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 déclencherai une erreur (car 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 doit donner la bonne valeur indépendamment de ndim. Comme complication supplémentaire, l'entrée peut être un flottant unique ou numpy.float64 Ou même quelque chose de bizarre comme un tableau à zéro dimension.

La façon évidente de résoudre ce problème est d'écrire une fonction récursive qui itère sur chaque objet itérable du tableau jusqu'à ce qu'il trouve un non-iterabe. Il appliquera la fonction numpy.isnan() sur chaque objet non itérable. Si au moins une valeur non numérique est trouvée, la fonction renverra immédiatement False. Sinon, si toutes les valeurs de l'itérable sont numériques, il retournera éventuellement True.

Cela fonctionne très bien, mais c'est assez lent et je m'attends à ce que NumPy ait une bien meilleure façon de le faire. Quelle est une alternative plus rapide et plus engourdie?

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
82
Salim Fadhley

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

numpy.isnan(myarray).any()

Édition: 30 fois 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 bien pour les types NumPy non matriciels:

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

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

numpy.isfinite(myarray).all()

Si ce qui précède est évalué à True, alors myarray ne contient pas, numpy.nan, numpy.inf ou -numpy.inf valeurs.

numpy.nan sera OK avec numpy.inf valeurs, 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)
13
Akavall

Avec numpy 1.3 ou svn vous pouvez le faire

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 antérieures.

3
chuck

(np.where(np.isnan(A)))[0].shape[0] sera supérieur à 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"
2
Ting On Chan

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

Notez que la réponse acceptée:

  • itère sur l'ensemble des données, peu importe si un nan est trouvé
  • crée un tableau temporaire de taille N, 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 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 est une accélération de 3 ordres ou de l'amplitude (dans certains cas). Pas trop minable pour une simple annotation.

1
user48956