Liczenie elementów innych niż NaN w numpy ndarray w Pythonie

87

Muszę obliczyć liczbę elementów innych niż NaN w macierzy numpy ndarray. Jak można to skutecznie zrobić w Pythonie? Oto mój prosty kod do osiągnięcia tego:

import numpy as np

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

Czy jest do tego wbudowana funkcja w Numpy? Wydajność jest ważna, ponieważ zajmuję się analizą Big Data.

Dziękuję za pomoc!

jjepsuomi
źródło
2
To pytanie wydaje się być off-topic, ponieważ należy na codereview.stackexchange.com
jonrsharpe
1
Masz na myśli wydajne pod względem pamięci?
Ashwini Chaudhary,
+1 Myślałem o czasie procesora, ale tak, dlaczego nie również pamięci. Im szybciej i taniej, tym lepiej =)
jjepsuomi
3
@jjepsuomi Będzie to wersja wydajna sum(not np.isnan(x) for x in a)pod względem pamięci , ale pod względem szybkości jest wolna w porównaniu z wersją @ M4rtini numpy.
Ashwini Chaudhary,
@AshwiniChaudhary Dziękuję bardzo! Muszę zobaczyć, który z nich jest ważniejszy w mojej aplikacji =)
jjepsuomi

Odpowiedzi:

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

~odwraca macierz boolowską zwróconą z np.isnan.

np.count_nonzerozlicza wartości inne niż 0 \ false. .sumpowinien dać ten sam wynik. Ale może jaśniej w użyciucount_nonzero

Prędkość testowania:

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))wydaje się być tutaj ledwo najszybszy. inne dane mogą dawać inne względne wyniki prędkości.

M4rtini
źródło
+1 @ M4rtini jeszcze raz dziękuję! Jesteś świetny! ;
D
3
Może nawet numpy.isnan(array).sum()? Nie jestem jednak biegły w odrętwieniu.
msvalkon
2
@msvalkon, Zlicza liczbę NaN, podczas gdy OP chce liczbę elementów innych niż NaN.
falsetru
2
@goncalopp stackoverflow.com/questions/8305199/… =)
jjepsuomi
5
Rozszerzenie odpowiedzi @msvalkon: data.size - np.isnan(data).sum()będzie nieco wydajniejsze.
Daniel
10

Alternatywa do szybkiego zapisu

Chociaż nie jest to najszybszy wybór, jeśli wydajność nie jest problemem, możesz użyć:

sum(~np.isnan(data)).

Występ:

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
GM
źródło
Ta odpowiedź dostarcza sumę, która nie jest tym samym, co zliczanie liczby elementów ... Zamiast tego należy użyć len.
BenT
@BenT suma elementów tablicy bool, które spełniają określony warunek, jest taka sama, pod warunkiem, że len tablicy podzbioru zawiera elementy spełniające określony warunek. Czy możesz wyjaśnić, gdzie to jest nie tak?
GM
1
Mój błąd zapomniałem, że Boolean otrzymał zwrot.
BenT
3

Aby określić, czy tablica jest rzadka, pomocne może być uzyskanie proporcji wartości nan

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

Jeśli ta proporcja przekracza próg, użyj rzadkiej tablicy, np. - https://sparse.pydata.org/en/latest/

Darren Weber
źródło
2

Alternatywą, ale nieco wolniejszą, jest wykonanie tego przez indeksowanie.

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 

Podwójne zastosowanie np.isnan(data), a ==operator może być trochę przesada i tak napisałem odpowiedź tylko dla kompletności.

Manuel
źródło