Zählen der Anzahl von Nicht-NaN-Elementen in einem numpy ndarray in Python


90

Ich muss die Anzahl der Nicht-NaN-Elemente in einer Numpy-Ndarray-Matrix berechnen. Wie würde man das in Python effizient machen? Hier ist mein einfacher Code, um dies zu erreichen:

import numpy as np

def numberOfNonNans(data):
    count = 0
    for i in data:
        if not np.isnan(i):
            count += 1
    return count 

Gibt es dafür eine eingebaute Funktion in numpy? Effizienz ist wichtig, weil ich Big Data-Analysen durchführe.

Danke für jede Hilfe!


2
Diese Frage erscheint-Thema aus sein , weil es auf gehört codereview.stackexchange.com
jonrsharpe

1
Sie meinen speichereffizient?
Ashwini Chaudhary

+1 Ich habe über die CPU-Zeit nachgedacht, aber warum nicht auch Speicher? Je schneller und billiger
desto

3
@jjepsuomi Eine speichereffiziente Version wird es sein sum(not np.isnan(x) for x in a), aber in Bezug auf die Geschwindigkeit ist sie langsam im Vergleich zur @ M4rtini-Numpy-Version.
Ashwini Chaudhary

@AshwiniChaudhary Vielen Dank! Ich muss sehen, welches in meiner Anwendung wichtiger ist =)
jjepsuomi

Antworten:


164
np.count_nonzero(~np.isnan(data))

~invertiert die von zurückgegebene boolesche Matrix np.isnan.

np.count_nonzerozählt Werte, die nicht 0 \ false sind. .sumsollte das gleiche Ergebnis geben. Aber vielleicht klarer zu bedienencount_nonzero

Testgeschwindigkeit:

In [23]: data = np.random.random((10000,10000))

In [24]: data[[np.random.random_integers(0,10000, 100)],:][:, [np.random.random_integers(0,99, 100)]] = np.nan

In [25]: %timeit data.size - np.count_nonzero(np.isnan(data))
1 loops, best of 3: 309 ms per loop

In [26]: %timeit np.count_nonzero(~np.isnan(data))
1 loops, best of 3: 345 ms per loop

In [27]: %timeit data.size - np.isnan(data).sum()
1 loops, best of 3: 339 ms per loop

data.size - np.count_nonzero(np.isnan(data))scheint hier kaum der schnellste zu sein. Andere Daten können zu unterschiedlichen Ergebnissen der relativen Geschwindigkeit führen.


+1 @ M4rtini Nochmals vielen Dank! Du bist toll! ; DI wird Ihre Antwort so schnell wie möglich annehmen :)
jjepsuomi

3
Vielleicht sogar numpy.isnan(array).sum()? Ich bin allerdings nicht sehr gut mit Numpy.
Msvalkon

2
@msvalkon, Es wird die Anzahl der NaN gezählt, während OP die Anzahl der Nicht-NaN-Elemente wünscht.
Falsetru


5
Eine Erweiterung von @msvalkon Antwort: data.size - np.isnan(data).sum()wird etwas effizienter sein.
Daniel

11

Schnell zu schreibende Alternative

Auch wenn dies nicht die schnellste Wahl ist, können Sie Folgendes verwenden, wenn die Leistung kein Problem darstellt:

sum(~np.isnan(data)).

Performance:

In [7]: %timeit data.size - np.count_nonzero(np.isnan(data))
10 loops, best of 3: 67.5 ms per loop

In [8]: %timeit sum(~np.isnan(data))
10 loops, best of 3: 154 ms per loop

In [9]: %timeit np.sum(~np.isnan(data))
10 loops, best of 3: 140 ms per loop

Diese Antwort liefert die Summe, die nicht mit dem Zählen der Anzahl der Elemente identisch ist ... Sie sollten lenstattdessen verwenden.
BenT

1
@BenT Die Summe der Elemente eines Bool-Arrays, die eine bestimmte Bedingung erfüllen, ist dieselbe, wobei die Länge eines Teilmengen-Arrays mit den Elementen versehen wird, die eine bestimmte Bedingung erfüllen. Können Sie bitte klarstellen, wo dies falsch ist?
GM

2
Mein Fehler Ich habe vergessen, dass ein Boolescher zurückgekommen ist.
BenT

3

Eine Alternative, aber etwas langsamer, ist die Indizierung.

np.isnan(data)[np.isnan(data) == False].size

In [30]: %timeit np.isnan(data)[np.isnan(data) == False].size
1 loops, best of 3: 498 ms per loop 

Die doppelte Verwendung von np.isnan(data)und der ==Operator könnten etwas übertrieben sein, und deshalb habe ich die Antwort nur der Vollständigkeit halber veröffentlicht.


3

Um festzustellen, ob das Array dünn ist, kann es hilfreich sein, einen Anteil der Nanowerte zu erhalten

np.isnan(ndarr).sum() / ndarr.size

Wenn dieser Anteil einen Schwellenwert überschreitet, verwenden Sie ein spärliches Array, z. B. - https://sparse.pydata.org/en/latest/

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.