В случае, если вам нужно действительно быстро для больших массивов, вы можете даже использовать numbas prange
для параллельной обработки счета (для небольших массивов это будет медленнее из-за издержек параллельной обработки).
import numpy as np
from numba import njit, prange
@njit(parallel=True)
def parallel_nonzero_count(arr):
flattened = arr.ravel()
sum_ = 0
for i in prange(flattened.size):
sum_ += flattened[i] != 0
return sum_
Обратите внимание, что когда вы используете numba, вы обычно хотите записывать свои циклы, потому что именно это numba действительно очень хорошо умеет оптимизировать.
Я фактически сравнил это с другими решениями, упомянутыми здесь.(используя мой модуль Python simple_benchmark
):
Код для воспроизведения:
import numpy as np
from numba import njit, prange
@njit
def n_nonzero(a):
return a[a != 0].size
@njit
def count_non_zero(np_arr):
return len(np.nonzero(np_arr)[0])
@njit()
def methodB(a):
return (a!=0).sum()
@njit(parallel=True)
def parallel_nonzero_count(arr):
flattened = arr.ravel()
sum_ = 0
for i in prange(flattened.size):
sum_ += flattened[i] != 0
return sum_
@njit()
def count_loop(a):
s = 0
for i in a:
if i != 0:
s += 1
return s
from simple_benchmark import benchmark
args = {}
for exp in range(2, 20):
size = 2**exp
arr = np.random.random(size)
arr[arr < 0.3] = 0.0
args[size] = arr
b = benchmark(
funcs=(n_nonzero, count_non_zero, methodB, np.count_nonzero, parallel_nonzero_count, count_loop),
arguments=args,
argument_name='array size',
warmups=(n_nonzero, count_non_zero, methodB, np.count_nonzero, parallel_nonzero_count, count_loop)
)