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
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
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)
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.
(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"
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:
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.