Erkennen, ob ein NumPy-Array mindestens einen nicht numerischen Wert enthält?

103

Ich muss eine Funktion schreiben, die erkennt, ob die Eingabe mindestens einen nicht numerischen Wert enthält. Wenn ein nicht numerischer Wert gefunden wird, wird ein Fehler ausgegeben (da die Berechnung nur einen numerischen Wert zurückgeben sollte). Die Anzahl der Dimensionen des Eingabearrays ist nicht im Voraus bekannt - die Funktion sollte unabhängig von ndim den richtigen Wert liefern. Als zusätzliche Komplikation könnte die Eingabe ein einzelner Float numpy.float64oder sogar etwas Seltsames wie ein nulldimensionales Array sein.

Die naheliegende Möglichkeit, dies zu lösen, besteht darin, eine rekursive Funktion zu schreiben, die über jedes iterierbare Objekt im Array iteriert, bis ein Nicht-Iterabe gefunden wird. Die numpy.isnan()Funktion wird auf jedes nicht iterierbare Objekt angewendet. Wenn mindestens ein nicht numerischer Wert gefunden wird, gibt die Funktion sofort False zurück. Andernfalls wird, wenn alle Werte in der Iterable numerisch sind, schließlich True zurückgegeben.

Das funktioniert gut, aber es ist ziemlich langsam und ich gehe davon aus, dass NumPy einen viel besseren Weg hat, dies zu tun. Was ist eine Alternative, die schneller und numpyischer ist?

Hier ist mein Modell:

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
Salim Fadhley
quelle
3
Ihre Beschreibung für contains_nansieht verdächtig aus: "Gibt false zurück, wenn mindestens ein nicht numerischer Wert vorhanden ist". Ich hätte erwartet contains_nanzurückzukehren, Truewenn das Array NaN enthält.
Samuel Tardieu
Was ist mit Eingaben wie array(['None', 'None'], dtype=object)? Sollte eine solche Eingabe nur eine Ausnahme auslösen?
Finn Årup Nielsen
NICHT verwenden float('nan') in x. Es funktioniert nicht.
Charlie Parker

Antworten:

181

Dies sollte schneller als das Iterieren sein und funktioniert unabhängig von der Form.

numpy.isnan(myarray).any()

Bearbeiten: 30x schneller:

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

Ergebnisse:

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

Bonus: Es funktioniert gut für NumPy-Typen ohne Array:

>>> a = numpy.float64(42.)
>>> numpy.isnan(a).any()
False
>>> a = numpy.float64(numpy.nan)
>>> numpy.isnan(a).any()
True
Paul
quelle
1
mit numpy 1.7 ist die flatten () version nur doppelt so schnell wie die erste
Christian Geier
Warum funktioniert so etwas float('nan') in xnicht nicht? Ich versuchte es und Python zurückkehrt , Falsewo x = [1,2,3,float('nan')].
Charlie Parker
1
@CharlieParker der gleiche Grund, warum float ('nan') == float ('nan') False zurückgibt. NaN ist nicht gleich NaN. Hier mehr Infos: stackoverflow.com/questions/10034149/…
Muppet
1
@mab: Das liegt daran, dass das Aufrufen numpy.anyeines Genexp nur den Genexp zurückgibt. Sie führen nicht die Berechnung durch, von der Sie glauben, dass Sie sie sind. Rufen Sie niemals numpy.anyeinen Genexp an.
user2357112 unterstützt Monica
In einem realen Debugging-Szenario würde ich auch empfehlen, zu schauen, np.isfiniteanstatt np.isnannumerische Überläufe, Instabilität usw. zu erkennen.
Ben Usman
18

Wenn unendlich ein möglicher Wert ist, würde ich numpy.isfinite verwenden

numpy.isfinite(myarray).all()

Wenn das oben Gesagte zu ausgewertet wird True, myarrayenthält no numpy.nan, numpy.infoder -numpy.infWerte.

numpy.nanwird mit numpy.infWerten in Ordnung sein , zum Beispiel:

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)
Akavall
quelle
Warum funktioniert so etwas float('nan') in xnicht nicht? Ich versuchte es und Python zurückkehrt , Falsewo x = [1,2,3,float('nan')].
Charlie Parker
1
@CharlieParker, weil zwei nans nicht als gleich angesehen werden. Versuchen Sie es float('nan') == float('nan').
Akavall
interessant. Warum werden sie nicht als gleich angesehen?
Charlie Parker
1
@CharlieParker, ich glaube nicht, dass ich hier eine sehr gute Antwort geben könnte. Vielleicht ist dies das, wonach Sie suchen: stackoverflow.com/questions/1565164/…
Akavall
4

Pfft! Mikrosekunden! Lösen Sie niemals ein Problem in Mikrosekunden, das in Nanosekunden gelöst werden kann.

Beachten Sie, dass die akzeptierte Antwort:

  • iteriert über die gesamten Daten, unabhängig davon, ob ein Nan gefunden wird
  • Erstellt ein temporäres Array der Größe N, das redundant ist.

Eine bessere Lösung besteht darin, True sofort zurückzugeben, wenn NAN gefunden wird:

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)

und arbeitet für n-Dimensionen:

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

Vergleichen Sie dies mit der numpy nativen Lösung:

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

Die Early-Exit-Methode ist eine Beschleunigung um 3 Größenordnungen oder Größenordnungen (in einigen Fällen). Nicht zu schäbig für eine einfache Anmerkung.

user48956
quelle
3

Mit numpy 1.3 oder svn können Sie dies tun

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

Die Behandlung von Nans in Vergleichen war in früheren Versionen nicht konsistent.


quelle
Warum funktioniert so etwas float('nan') in xnicht nicht? Ich versuchte es und Python zurückkehrt , Falsewo x = [1,2,3,float('nan')].
Charlie Parker
@CharlieParker ... weil der Vergleich mit NAN nicht das tut, was Sie erwarten. NAN wird wie ein logischer NULL behandelt (= weiß nicht). float("nan")==float("nan")geben False(obwohl machbar, sollte es wahrscheinlich NAN oder Keine zurückgeben). In ähnlicher Weise ist die Seltsamkeit mit NAN und Boolen NULL in vielen Sprachen wahr, einschließlich SQL (wobei NULL = NULL niemals wahr ist).
user48956
2

(np.where(np.isnan(A)))[0].shape[0]wird größer sein als 0wenn es Amindestens ein Element enthält nan, Akönnte eine n x mMatrix sein.

Beispiel:

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"
Ting On Chan
quelle