Подсчет количества элементов, отличных от NaN, в numpy ndarray в Python

87

Мне нужно вычислить количество элементов, отличных от NaN, в матрице numpy ndarray. Как можно эффективно сделать это в Python? Вот мой простой код для этого:

import numpy as np

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

Есть ли для этого встроенная функция в numpy? Эффективность важна, потому что я занимаюсь анализом больших данных.

Спасибо за любую помощь!

Jjepsuomi
источник
2
Этот вопрос кажется не по теме, потому что он принадлежит codereview.stackexchange.com
jonrsharpe
1
Вы имеете в виду эффективный с точки зрения памяти?
Ашвини Чаудхари
+1 Я думал о процессоре, но почему бы и не о памяти. Чем быстрее и дешевле, тем лучше =)
jjepsuomi
3
@jjepsuomi Будет версия с эффективным использованием памяти sum(not np.isnan(x) for x in a), но с точки зрения скорости она медленная по сравнению с версией @ M4rtini numpy.
Ашвини Чаудхари
@AshwiniChaudhary Большое спасибо! Мне нужно посмотреть, какой из них важнее в моем приложении =)
jjepsuomi

Ответы:

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

~инвертирует булеву матрицу, возвращенную из np.isnan.

np.count_nonzeroсчитает значения, отличные от 0 \ false. .sumдолжен дать такой же результат. Но, может быть, более понятно использоватьcount_nonzero

Скорость тестирования:

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))здесь едва ли может быть самым быстрым. другие данные могут дать другие результаты относительной скорости.

M4rtini
источник
+1 @ M4rtini еще раз спасибо! Ты замечательный! ;
D
3
Может даже numpy.isnan(array).sum()? Хотя я не очень разбираюсь в numpy.
msvalkon
2
@msvalkon, он посчитает количество NaN, в то время как OP хочет количество элементов, отличных от NaN.
falsetru
2
@goncalopp stackoverflow.com/questions/8305199/… =)
jjepsuomi
5
Расширение ответа @msvalkon: data.size - np.isnan(data).sum()будет немного эффективнее.
Daniel
10

Быстро пишущая альтернатива

Хотя это не самый быстрый выбор, если производительность не является проблемой, вы можете использовать:

sum(~np.isnan(data)).

Производительность:

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
источник
Этот ответ дает сумму, которая отличается от подсчета количества элементов ... lenВместо этого вы должны использовать .
BenT
@BenT сумма элементов массива bool, удовлетворяющих определенному условию, такая же, как при условии, что длина массива подмножества с элементами, которые удовлетворяют определенному условию. Не могли бы вы пояснить, где это не так?
GM
1
Моя ошибка: я забыл, что возвращено логическое значение.
BenT
3

Чтобы определить, является ли массив разреженным, может помочь получить пропорцию значений nan

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

Если эта доля превышает пороговое значение, используйте разреженный массив, например - https://sparse.pydata.org/en/latest/

Даррен Вебер
источник
2

Альтернатива, но немного более медленная - это сделать это вместо индексации.

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 

Двойное использование np.isnan(data)и ==оператора может быть немного излишним, поэтому я опубликовал ответ только для полноты.

Мануэль
источник