Menghitung jumlah elemen non-NaN dalam ndarray numpy dengan Python

90

Saya perlu menghitung jumlah elemen non-NaN dalam matriks ndarray numpy. Bagaimana cara melakukan ini secara efisien dengan Python? Inilah kode sederhana saya untuk mencapai ini:

import numpy as np

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

Apakah ada fungsi bawaan untuk ini di numpy? Efisiensi itu penting karena saya melakukan analisis Big Data.

Terima kasih atas bantuannya!

jjepsuomi
sumber
2
Pertanyaan ini tampaknya di luar topik karena termasuk dalam codereview.stackexchange.com
jonrsharpe
1
Maksud Anda efisien dalam hal memori?
Ashwini Chaudhary
+1 Saya sedang berpikir tentang waktu CPU, tapi ya mengapa tidak memori juga. Lebih cepat dan lebih murah lebih baik =)
jjepsuomi
3
@jjepsuomi Sebuah versi yang hemat memori sum(not np.isnan(x) for x in a), tetapi dalam hal kecepatannya lambat dibandingkan dengan versi numpy @ M4rtini.
Ashwini Chaudhary
@AshwiniChaudhary Terima kasih banyak! Saya perlu melihat mana yang lebih penting dalam lamaran saya =)
jjepsuomi

Jawaban:

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

~membalikkan matriks boolean yang dikembalikan np.isnan.

np.count_nonzeromenghitung nilai yang bukan 0 \ false. .sumharus memberikan hasil yang sama. Tapi mungkin lebih jelas untuk digunakancount_nonzero

Kecepatan pengujian:

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))sepertinya hampir tidak menjadi yang tercepat di sini. data lain mungkin memberikan hasil kecepatan relatif yang berbeda.

M4rtini
sumber
+1 @ M4rtini terima kasih lagi! Kamu hebat! ; DI akan menerima jawaban Anda secepat saya bisa :)
jjepsuomi
3
Bahkan mungkin numpy.isnan(array).sum()? Saya tidak terlalu mahir dengan numpy.
msvalkon
2
@msvalkon, Ini akan menghitung jumlah NaN, sedangkan OP menginginkan jumlah elemen non-NaN.
falsetru
2
@goncalopp stackoverflow.com/questions/8305199/… =)
jjepsuomi
5
Perpanjangan jawaban @msvalkon: data.size - np.isnan(data).sum()akan sedikit lebih efisien.
Daniel
11

Alternatif cepat untuk menulis

Meskipun bukan pilihan tercepat, jika kinerja bukan masalah, Anda dapat menggunakan:

sum(~np.isnan(data)).

Performa:

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
sumber
Jawaban ini memberikan jumlah yang tidak sama dengan menghitung jumlah elemen ... Anda harus menggunakan lensebagai gantinya.
BenT
1
@BenT Jumlah elemen array bool yang memenuhi kondisi tertentu sama dengan memberikan len dari subset array dengan elemen yang memenuhi kondisi tertentu. Bisakah Anda menjelaskan di mana kesalahan ini?
GM
2
Kesalahan saya, saya lupa Boolean dikembalikan.
BenT
3

Alternatif, tetapi alternatif yang sedikit lebih lambat adalah melakukannya melalui pengindeksan.

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 

Penggunaan ganda np.isnan(data)dan ==operator mungkin sedikit berlebihan dan jadi saya memposting jawaban hanya untuk kelengkapan.

Manuel
sumber
3

Untuk menentukan apakah lariknya jarang, mungkin membantu untuk mendapatkan proporsi nilai nan

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

Jika proporsi itu melebihi ambang batas, gunakan larik renggang, misalnya - https://sparse.pydata.org/en/latest/

Darren Weber
sumber