Teste se a matriz numpy contém apenas zeros

92

Inicializamos uma matriz numpy com zeros conforme abaixo:

np.zeros((N,N+1))

Mas como verificamos se todos os elementos em uma determinada matriz n * n numpy array são zero.
O método só precisa retornar um True se todos os valores forem realmente zero.

IUnknown
fonte

Respostas:

71

Confira numpy.count_nonzero .

>>> np.count_nonzero(np.eye(4))
4
>>> np.count_nonzero([[0,1,7,0,0],[3,0,0,2,19]])
5
Prashant Kumar
fonte
9
Você gostaria not np.count_nonzero(np.eye(4))de retornar Trueapenas se todos os valores forem 0.
J. Martinot-Lagarde
161

As outras respostas postadas aqui funcionarão, mas a função mais clara e eficiente de usar é numpy.any():

>>> all_zeros = not np.any(a)

ou

>>> all_zeros = not a.any()
  • É preferível numpy.all(a==0)porque usa menos RAM. (Não requer a matriz temporária criada pelo a==0termo.)
  • Além disso, é mais rápido do que numpy.count_nonzero(a)porque pode retornar imediatamente quando o primeiro elemento diferente de zero for encontrado.
    • Edit: Como @Rachel apontou nos comentários, np.any()não usa mais a lógica de "curto-circuito", então você não verá um benefício de velocidade para pequenos arrays.
Stuart Berg
fonte
2
A partir de um minuto atrás, numpy do anye allfazer não curto-circuito. Eu acredito que eles são açúcar para logical_or.reducee logical_and.reduce. Compare um ao outro e meu curto-circuito is_in: all_false = np.zeros(10**8) all_true = np.ones(10**8) %timeit np.any(all_false) 91.5 ms ± 1.82 ms per loop %timeit np.any(all_true) 93.7 ms ± 6.16 ms per loop %timeit is_in(1, all_true) 293 ns ± 1.65 ns per loop
Rachel
2
Esse é um ótimo ponto, obrigado. Parece que o comportamento costumava ser o curto-circuito , mas isso se perdeu em algum momento. Há alguma discussão interessante nas respostas a esta pergunta .
Stuart Berg
50

Eu usaria np.all aqui, se você tiver uma matriz a:

>>> np.all(a==0)
J. Martinot-Lagarde
fonte
3
Gosto que esta resposta verifique também valores diferentes de zero. Por exemplo, pode-se verificar se todos os elementos em um array são iguais fazendo np.all(a==a[0]). Muito obrigado!
aignas
9

Como outra resposta diz, você pode tirar proveito de avaliações verdadeiras / falsas se souber que esse 0é o único elemento falso possivelmente em seu array. Todos os elementos em uma matriz são falsos se não houver elementos verdadeiros nela. *

>>> a = np.zeros(10)
>>> not np.any(a)
True

No entanto, a resposta alegou que anyfoi mais rápido do que outras opções devido em parte ao curto-circuito. Em 2018, Numpy's alleany não curto-circuite .

Se você faz esse tipo de coisa com frequência, é muito fácil fazer suas próprias versões de curto-circuito usando numba:

import numba as nb

# short-circuiting replacement for np.any()
@nb.jit(nopython=True)
def sc_any(array):
    for x in array.flat:
        if x:
            return True
    return False

# short-circuiting replacement for np.all()
@nb.jit(nopython=True)
def sc_all(array):
    for x in array.flat:
        if not x:
            return False
    return True

Eles tendem a ser mais rápidos do que as versões do Numpy, mesmo quando não estão em curto-circuito. count_nonzeroé o mais lento.

Algumas informações para verificar o desempenho:

import numpy as np

n = 10**8
middle = n//2
all_0 = np.zeros(n, dtype=int)
all_1 = np.ones(n, dtype=int)
mid_0 = np.ones(n, dtype=int)
mid_1 = np.zeros(n, dtype=int)
np.put(mid_0, middle, 0)
np.put(mid_1, middle, 1)
# mid_0 = [1 1 1 ... 1 0 1 ... 1 1 1]
# mid_1 = [0 0 0 ... 0 1 0 ... 0 0 0]

Verifica:

## count_nonzero
%timeit np.count_nonzero(all_0) 
# 220 ms ± 8.73 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.count_nonzero(all_1)
# 150 ms ± 4.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

### all
# np.all
%timeit np.all(all_1)
%timeit np.all(mid_0)
%timeit np.all(all_0)
# 56.8 ms ± 3.41 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 57.4 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 55.9 ms ± 2.13 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# sc_all
%timeit sc_all(all_1)
%timeit sc_all(mid_0)
%timeit sc_all(all_0)
# 44.4 ms ± 2.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 22.7 ms ± 599 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 288 ns ± 6.36 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

### any
# np.any
%timeit np.any(all_0)
%timeit np.any(mid_1)
%timeit np.any(all_1)
# 60.7 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 60 ms ± 287 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 57.7 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# sc_any
%timeit sc_any(all_0)
%timeit sc_any(mid_1)
%timeit sc_any(all_1)
# 41.7 ms ± 1.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 22.4 ms ± 1.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 287 ns ± 12.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

* Útil alle anyequivalências:

np.all(a) == np.logical_not(np.any(np.logical_not(a)))
np.any(a) == np.logical_not(np.all(np.logical_not(a)))
not np.all(a) == np.any(np.logical_not(a))
not np.any(a) == np.all(np.logical_not(a))
Rachel
fonte
-9

Se você estiver testando todos os zeros para evitar um aviso em outra função numpy, tente quebrar a linha em uma tentativa, exceto o bloco que evitará ter que fazer o teste para zeros antes da operação em que você está interessado, ou seja,

try: # removes output noise for empty slice 
    mean = np.mean(array)
except:
    mean = 0
ReaddyEddy
fonte