Rileva se un array NumPy contiene almeno un valore non numerico?


103

Ho bisogno di scrivere una funzione che rileverà se l'input contiene almeno un valore che non è numerico. Se viene trovato un valore non numerico, solleverò un errore (perché il calcolo dovrebbe restituire solo un valore numerico). Il numero di dimensioni dell'array di input non è noto in anticipo: la funzione dovrebbe fornire il valore corretto indipendentemente da ndim. Come ulteriore complicazione, l'input potrebbe essere un singolo float numpy.float64o anche qualcosa di strano come un array a dimensione zero.

Il modo ovvio per risolvere questo problema è scrivere una funzione ricorsiva che itera su ogni oggetto iterabile nell'array finché non trova un non iterabile. Applicherà la numpy.isnan()funzione su ogni oggetto non iterabile. Se viene trovato almeno un valore non numerico, la funzione restituirà immediatamente False. Altrimenti, se tutti i valori nell'iterabile sono numerici, alla fine restituirà True.

Funziona perfettamente, ma è piuttosto lento e mi aspetto che NumPy abbia un modo molto migliore per farlo. Qual è un'alternativa più veloce e più insensibile?

Ecco il mio mockup:

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

3
La tua descrizione per contains_nansembra sospetta: "Restituisce falso se esiste almeno un valore non numerico". Mi sarei aspettato contains_nandi tornare Truese l'array contiene NaN.
Samuel Tardieu

E gli input come array(['None', 'None'], dtype=object)? Un tale input dovrebbe sollevare solo un'eccezione?
Finn Årup Nielsen

NON usare float('nan') in x. Non funziona.
Charlie Parker

Risposte:


183

Dovrebbe essere più veloce dell'iterazione e funzionerà indipendentemente dalla forma.

numpy.isnan(myarray).any()

Modifica: 30 volte più veloce:

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

Risultati:

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

Bonus: funziona bene per i tipi NumPy non array:

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

1
con numpy 1.7 la versione flatten () è solo due volte più veloce della prima
Christian Geier

Perché qualcosa come float('nan') in xnon funziona? L'ho provato e Python restituisce Falsedove x = [1,2,3,float('nan')].
Charlie Parker

1
@CharlieParker lo stesso motivo per cui float ('nan') == float ('nan') restituirà False. NaN non è uguale a NaN. Qui maggiori informazioni: stackoverflow.com/questions/10034149/…
Muppet

1
@mab: Questo perché invocare numpy.anyun genexp restituisce solo genexp; in realtà non stai facendo il calcolo che pensi di essere. Non chiamare mai numpy.anyun genexp.
user2357112 supporta Monica

Nello scenario di debug reale, consiglierei anche di guardare np.isfiniteinvece di np.isnanrilevare overflow numerici, instabilità, ecc.
Ben Usman

18

Se infinity è un valore possibile, userei numpy.isfinite

numpy.isfinite(myarray).all()

Se quanto sopra restituisce True, allora myarraynon contiene i valori numpy.nan, numpy.info -numpy.inf.

numpy.nanandrà bene con i numpy.infvalori, ad esempio:

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)

Perché qualcosa come float('nan') in xnon funziona? L'ho provato e Python restituisce Falsedove x = [1,2,3,float('nan')].
Charlie Parker

1
@CharlieParker perché due nannon sono considerati uguali tra loro. Prova float('nan') == float('nan').
Akavall

interessante. Perché non sono considerati uguali?
Charlie Parker

1
@CharlieParker, non credo di poter dare una risposta molto buona qui. Forse questo è quello che stai cercando: stackoverflow.com/questions/1565164/...
Akavall

4

Pfft! Microsecondi! Non risolvere mai un problema in microsecondi che può essere risolto in nanosecondi.

Nota che la risposta accettata:

  • itera su tutti i dati, indipendentemente dal fatto che venga trovato un nan
  • crea un array temporaneo di dimensione N, che è ridondante.

Una soluzione migliore è restituire True immediatamente quando viene trovato NAN:

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)

e funziona per n-dimensioni:

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

Confronta questo con la soluzione nativa 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

Il metodo di uscita anticipata è di 3 ordini o magnitudo accelerazione (in alcuni casi). Non troppo squallido per una semplice annotazione.


3

Con numpy 1.3 o svn puoi farlo

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

Il trattamento dei nans nei confronti non era coerente nelle versioni precedenti.


Perché qualcosa come float('nan') in xnon funziona? L'ho provato e Python restituisce Falsedove x = [1,2,3,float('nan')].
Charlie Parker

@CharlieParker ... perché il confronto con NAN non fa quello che ti aspetti. NAN è trattato come un NULL logico (= non so). float("nan")==float("nan")dare False(anche se fattibile dovrebbe probabilmente restituire NAN o Nessuno). Allo stesso modo la stranezza con NAN e boolen NULL è vera in molte lingue, incluso SQL (dove NULL = NULL non è mai vero).
user48956

2

(np.where(np.isnan(A)))[0].shape[0]sarà maggiore di 0se Acontiene almeno un elemento di nan, Apotrebbe essere una n x mmatrice.

Esempio:

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"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.