Como contar a ocorrência de determinado item em um ndarray no Python?


376

No Python, eu tenho um ndarray y impresso comoarray([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

Estou tentando contar quantos se há 0quantos 1nesta matriz.

Mas quando eu digito y.count(0)ou y.count(1), diz

numpy.ndarray objeto não tem atributo count

O que devo fazer?


8
Você não pode usar a função soma e comprimento, pois só possui ases e zeros?
codingEnthusiast

Nesse caso, também é possível simplesmente usar numpy.count_nonzero.
Mong H. Ng

Respostas:


610
>>> a = numpy.array([0, 3, 0, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 3, 4])
>>> unique, counts = numpy.unique(a, return_counts=True)
>>> dict(zip(unique, counts))
{0: 7, 1: 4, 2: 1, 3: 2, 4: 1}

Maneira não-numpy :

Use collections.Counter;

>> import collections, numpy

>>> a = numpy.array([0, 3, 0, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 3, 4])
>>> collections.Counter(a)
Counter({0: 7, 1: 4, 3: 2, 2: 1, 4: 1})

3
Isso seria `` único, contagens = numpy.unique (a, return_counts = True) dict (zip (unique, counts)) `` ``
shredding

25
Se quiser que o dicionário,dict(zip(*numpy.unique(a, return_counts=True)))
Seppo Enarvi

2
E se eu quiser acessar o número de ocorrências de cada elemento exclusivo da matriz sem atribuir à contagem de variáveis. Alguma dica sobre isso?
sajis997

Eu tenho o mesmo objetivo que @ sajis997. Eu quero usar 'count' como uma função agregadora em um groupby
p_sutherland 15/03

11
Tentei usar os dois métodos para uma matriz muito grande (~ 30 GB). O método Numpy ficou sem memória, enquanto o collections.Counterfuncionou muito bem #
1919 Ivan Novikov

252

Que tal usar numpy.count_nonzeroalgo como

>>> import numpy as np
>>> y = np.array([1, 2, 2, 2, 2, 0, 2, 3, 3, 3, 0, 0, 2, 2, 0])

>>> np.count_nonzero(y == 1)
1
>>> np.count_nonzero(y == 2)
7
>>> np.count_nonzero(y == 3)
3

20
Essa resposta parece melhor do que aquela com mais votos positivos.
31417 Alex

11
Eu não acho que isso funcionaria numpy.ndarraycomo o OP originalmente pediu.
LYu

5
@LYu - o y é um np.ndarray nesta resposta. Além disso - a maioria, se não todas, as funções np.something funcionam em ndarrays sem problemas.
Mmagnuski 29/07

132

Pessoalmente, eu iria para: (y == 0).sum()e(y == 1).sum()

Por exemplo

import numpy as np
y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
num_zeros = (y == 0).sum()
num_ones = (y == 1).sum()

11
É definitivamente o mais fácil de ler. A questão é que é mais rápido e mais eficiente do espaço
Nathan

São susceptíveis de serem menos espaço eficiente do que numpy.count_nonzero (y == 0), uma vez que avalia o vector (y == 0)
Sridhar Thiagarajan

Eu gosto disso porque é semelhante ao matlab / oitavasum( vector==value )
ePi272314

39

Para o seu caso, você também pode procurar numpy.bincount

In [56]: a = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

In [57]: np.bincount(a)
Out[57]: array([8, 4])  #count of zeros is at index 0 : 8
                        #count of ones is at index 1 : 4

Esse código pode ser uma das soluções mais rápidas para matrizes maiores que experimentei. Obter o resultado como uma lista também é um bônus. Thanx!
Youngsup Kim

E se 'a' é uma matriz n-dimensional, podemos apenas usar: np.bincount (np.reshape (a, a.size))
Ari

21

Converta sua matriz yem lista le faça l.count(1)el.count(0)

>>> y = numpy.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
>>> l = list(y)
>>> l.count(1)
4
>>> l.count(0)
8 

19
y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

Se você sabe que eles são justos 0e 1:

np.sum(y)

fornece o número de unidades. np.sum(1-y)dá os zeros.

Por uma ligeira generalidade, se você deseja contar 0e não zero (mas possivelmente 2 ou 3):

np.count_nonzero(y)

fornece o número de diferente de zero.

Mas se você precisar de algo mais complicado, não acho que o numpy forneça uma boa countopção. Nesse caso, vá para coleções:

import collections
collections.Counter(y)
> Counter({0: 8, 1: 4})

Isso se comporta como um ditado

collections.Counter(y)[0]
> 8

13

Se você souber exatamente qual número está procurando, poderá usar o seguinte;

lst = np.array([1,1,2,3,3,6,6,6,3,2,1])
(lst == 2).sum()

retorna quantas vezes 2 ocorreu em sua matriz.


8

Honestamente, acho mais fácil converter para uma série de pandas ou DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame({'data':np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])})
print df['data'].value_counts()

Ou este belo artigo sugerido por Robert Muil:

pd.Series([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]).value_counts()

4
Apenas uma observação: não precisa do DataFrame ou numpy, pode ir diretamente de uma lista para uma série: pd.Series([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]).value_counts()
Robert Muil 02/02

Impressionante, é uma frase agradável. Big up
wordsforthewise

8

Ninguém sugeriu usar numpy.bincount(input, minlength)com minlength = np.size(input), mas parece ser uma boa solução, e definitivamente o mais rápido :

In [1]: choices = np.random.randint(0, 100, 10000)

In [2]: %timeit [ np.sum(choices == k) for k in range(min(choices), max(choices)+1) ]
100 loops, best of 3: 2.67 ms per loop

In [3]: %timeit np.unique(choices, return_counts=True)
1000 loops, best of 3: 388 µs per loop

In [4]: %timeit np.bincount(choices, minlength=np.size(choices))
100000 loops, best of 3: 16.3 µs per loop

Essa é uma aceleração louca entre numpy.unique(x, return_counts=True)e numpy.bincount(x, minlength=np.max(x))!


Como ele se compara ao histograma?
john ktejik

@johnktejik np.histogramnão calcula a mesma coisa. Não faz sentido comparar as três abordagens que proponho com a histogramfunção, desculpe.
Næreen

11
@ Næreen bincountfunciona apenas para números inteiros, portanto, funciona para o problema do OP, mas talvez não para o problema genérico descrito no título. Você também tentou usar bincountmatrizes com ints muito grandes?
Noite imperecível

@ImperishableNight Não, eu não tentei com grandes inteiros, mas qualquer um é bem-vindo a fazê-lo e postar a sua própria referência :-)
Næreen

Obrigado por este truque subvalorizado! Na minha máquina bincounté cerca de quatro vezes mais rápido que unique.
Björn Lindqvist


6

y.tolist().count(val)

com val 0 ou 1

Como uma lista python possui uma função nativa count, a conversão em lista antes de usar essa função é uma solução simples.


5

Ainda outra solução simples pode ser usar numpy.count_nonzero () :

import numpy as np
y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
y_nonzero_num = np.count_nonzero(y==1)
y_zero_num = np.count_nonzero(y==0)
y_nonzero_num
4
y_zero_num
8

Não deixe o nome te enganar, se você o usar com o booleano, como no exemplo, ele fará o truque.


5

Para contar o número de ocorrências, você pode usar np.unique(array, return_counts=True):

In [75]: boo = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

# use bool value `True` or equivalently `1`
In [77]: uniq, cnts = np.unique(boo, return_counts=1)
In [81]: uniq
Out[81]: array([0, 1])   #unique elements in input array are: 0, 1

In [82]: cnts
Out[82]: array([8, 4])   # 0 occurs 8 times, 1 occurs 4 times

4

Eu usaria np.where:

how_many_0 = len(np.where(a==0.)[0])
how_many_1 = len(np.where(a==1.)[0])

3

tire proveito dos métodos oferecidos por uma série:

>>> import pandas as pd
>>> y = [0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]
>>> pd.Series(y).value_counts()
0    8
1    4
dtype: int64

2

Uma resposta geral e simples seria:

numpy.sum(MyArray==x)   # sum of a binary list of the occurence of x (=0 or 1) in MyArray

o que resultaria nesse código completo como exemplo

import numpy
MyArray=numpy.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])  # array we want to search in
x=0   # the value I want to count (can be iterator, in a list, etc.)
numpy.sum(MyArray==0)   # sum of a binary list of the occurence of x in MyArray

Agora, se MyArray está em várias dimensões e você deseja contar a ocorrência de uma distribuição de valores na linha (= padrão a seguir)

MyArray=numpy.array([[6, 1],[4, 5],[0, 7],[5, 1],[2, 5],[1, 2],[3, 2],[0, 2],[2, 5],[5, 1],[3, 0]])
x=numpy.array([5,1])   # the value I want to count (can be iterator, in a list, etc.)
temp = numpy.ascontiguousarray(MyArray).view(numpy.dtype((numpy.void, MyArray.dtype.itemsize * MyArray.shape[1])))  # convert the 2d-array into an array of analyzable patterns
xt=numpy.ascontiguousarray(x).view(numpy.dtype((numpy.void, x.dtype.itemsize * x.shape[0])))  # convert what you search into one analyzable pattern
numpy.sum(temp==xt)  # count of the searched pattern in the list of patterns

2

Você pode usar a compreensão de dicionário para criar uma linha única. Mais informações sobre a compreensão do dicionário podem ser encontradas aqui

>>>counts = {int(value): list(y).count(value) for value in set(y)}
>>>print(counts)
{0: 8, 1: 4}

Isso criará um dicionário com os valores em seu ndarray como chaves e as contagens dos valores como valores para as chaves, respectivamente.

Isso funcionará sempre que você desejar contar ocorrências de um valor em matrizes desse formato.


2

Tente o seguinte:

a = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
list(a).count(1)

1

Isso pode ser feito facilmente no seguinte método

y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
y.tolist().count(1)

1

Como o ndarray contém apenas 0 e 1, você pode usar sum () para obter a ocorrência de 1s e len () - sum () para obter a ocorrência de 0s.

num_of_ones = sum(array)
num_of_zeros = len(array)-sum(array)

1

Você tem uma matriz especial com apenas 1 e 0 aqui. Então, um truque é usar

np.mean(x)

que fornece a porcentagem de 1s em sua matriz. Como alternativa, use

np.sum(x)
np.sum(1-x)

fornecerá o número absoluto de 1 e 0 em sua matriz.


1
dict(zip(*numpy.unique(y, return_counts=True)))

Apenas copiei o comentário de Seppo Enarvi aqui, que merece ser uma resposta adequada


0

Isso envolve mais uma etapa, mas uma solução mais flexível que também funcionaria para matrizes 2D e filtros mais complicados é criar uma máscara booleana e usar .sum () na máscara.

>>>>y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
>>>>mask = y == 0
>>>>mask.sum()
8

0

Se você não quiser usar o numpy ou um módulo de coleções, use um dicionário:

d = dict()
a = [0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]
for item in a:
    try:
        d[item]+=1
    except KeyError:
        d[item]=1

resultado:

>>>d
{0: 8, 1: 4}

Claro que você também pode usar uma instrução if / else. Eu acho que a função Counter faz quase a mesma coisa, mas isso é mais transparente.


0

Para entradas genéricas:

x = np.array([11, 2, 3, 5, 3, 2, 16, 10, 10, 3, 11, 4, 5, 16, 3, 11, 4])
n = {i:len([j for j in np.where(x==i)[0]]) for i in set(x)}
ix = {i:[j for j in np.where(x==i)[0]] for i in set(x)}

Irá gerar uma contagem:

{2: 2, 3: 4, 4: 2, 5: 2, 10: 2, 11: 3, 16: 2}

E índices:

{2: [1, 5],
3: [2, 4, 9, 14],
4: [11, 16],
5: [3, 12],
10: [7, 8],
11: [0, 10, 15],
16: [6, 13]}

0

aqui eu tenho algo, através do qual você pode contar o número de ocorrência de um número específico: de acordo com seu código

count_of_zero = lista (y [y == 0]). count (0)

print (count_of_zero)

// de acordo com a correspondência, haverá valores booleanos e de acordo com o valor True, o número 0 será retornado


0

Se você estiver interessado na execução mais rápida, você sabe com antecedência quais valores procurar e sua matriz é 1D ou se interessa pelo resultado da matriz achatada (nesse caso, a entrada da função deve seja em np.flatten(arr)vez de apenas arr), então Numba é seu amigo:

import numba as nb


@nb.jit
def count_nb(arr, value):
    result = 0
    for x in arr:
        if x == value:
            result += 1
    return result

ou, para matrizes muito grandes em que a paralelização pode ser benéfica:

@nb.jit(parallel=True)
def count_nbp(arr, value):
    result = 0
    for i in nb.prange(arr.size):
        if arr[i] == value:
            result += 1
    return result

Comparando-os np.count_nonzero()(que também tem um problema de criar uma matriz temporária que pode ser evitada) e np.unique()solução baseada em

import numpy as np


def count_np(arr, value):
    return np.count_nonzero(arr == value)
import numpy as np


def count_np2(arr, value):
    uniques, counts = np.unique(a, return_counts=True)
    counter = dict(zip(uniques, counts))
    return counter[value] if value in counter else 0 

para entrada gerada com:

def gen_input(n, a=0, b=100):
    return np.random.randint(a, b, n)

as seguintes plotagens são obtidas (a segunda linha de plotagens é um zoom na abordagem mais rápida):

bm_full bm_zoom

Mostrando que a solução baseada no Numba é visivelmente mais rápida que a do NumPy e, para entradas muito grandes, a abordagem paralela é mais rápida que a ingênua.


Código completo disponível aqui .


0

se você estiver lidando com matrizes muito grandes usando geradores, pode ser uma opção. O bom aqui é que essa abordagem funciona bem para matrizes e listas e você não precisa de nenhum pacote adicional. Além disso, você não está usando tanta memória.

my_array = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
sum(1 for val in my_array if val==0)
Out: 8

-1

Numpy tem um módulo para isso. Apenas um pequeno truque. Coloque sua matriz de entrada como caixas.

numpy.histogram(y, bins=y)

A saída são 2 matrizes. Um com os próprios valores, outro com as frequências correspondentes.


'caixas' não deveria ser um número?
john ktejik

11
Sim @johnktejik você está certo. Esta resposta não funciona.
Næreen

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.