Come contare l'occorrenza di un determinato oggetto in un ndarray in Python?


376

In Python, ho un ndarray y che viene stampato comearray([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

Sto provando a contare quanti se 0quanti 1s ci sono in questo array.

Ma quando scrivo y.count(0)o y.count(1), dice

numpy.ndarray l'oggetto non ha alcun attributo count

Cosa dovrei fare?


8
Non puoi usare la funzione somma e lunghezza, dato che hai solo assi e zeri?
coding Entusiasta il

In questo caso, è anche possibile semplicemente utilizzare numpy.count_nonzero.
Mong H. Ng

Risposte:


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}

Modo non intorpidito :

Utilizzare 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
Sarebbe `` unico, conteggi = numpy.unique (a, return_counts = True) dict (zip (unico, conteggi)) `` ''
distruggere il

25
Se vuoi il dizionario,dict(zip(*numpy.unique(a, return_counts=True)))
Seppo Enarvi,

2
E se volessi accedere al numero di occorrenze di ciascun elemento univoco dell'array senza assegnarlo alla variabile - conta. Qualche suggerimento al riguardo?
sajis997,

Ho lo stesso obiettivo di @ sajis997. Voglio usare 'count' come funzione di aggregazione in un groupby
p_sutherland

1
Ho provato usando entrambi i metodi per un array molto grande (~ 30Gb). Il metodo Numpy ha esaurito la memoria, mentre ha collections.Counterfunzionato bene
Ivan Novikov il

252

Che ne dici di usare numpy.count_nonzeroqualcosa del genere

>>> 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
Questa risposta sembra migliore di quella con il maggior numero di voti.
Alex,

1
Non penso che funzionerebbe numpy.ndarraycome inizialmente richiesto da OP.
LYu,

5
@LYu - y è un np.ndarray in questa risposta. Inoltre, la maggior parte se non tutte le funzioni np.something funzionano senza problemi su ndarrays.
mmagnuski,

132

Personalmente, sceglierei: (y == 0).sum()e(y == 1).sum()

Per esempio

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()

1
È sicuramente il più facile da leggere. La domanda è quale sia il più veloce e il più efficiente in termini di spazio
Nathan,

Potrebbe essere meno efficiente nello spazio rispetto a numpy.count_nonzero (y == 0), poiché valuta il vettore (y == 0)
Sridhar Thiagarajan,

Mi piace perché è simile a matlab / octavesum( vector==value )
ePi272314

39

Per il tuo caso, puoi anche consultare 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

Questo codice potrebbe essere una delle soluzioni più veloci per array più grandi che ho sperimentato. Ottenere il risultato come una lista è anche un bonus. Grazie!
Youngsup Kim,

E se 'a' è un array n-dimensionale, possiamo semplicemente usare: np.bincount (np.reshape (a, a.size))
Ari

21

Converti il ​​tuo array yin elenco le poi fai 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 sai che sono giusti 0e 1:

np.sum(y)

ti dà il numero di quelli. np.sum(1-y)dà gli zeri.

Per una piccola generalità, se vuoi contare 0e non zero (ma forse 2 o 3):

np.count_nonzero(y)

dà il numero di diverso da zero.

Ma se hai bisogno di qualcosa di più complicato, non credo che l'intorpidimento fornirà una buona countopzione. In tal caso, vai alle raccolte:

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

Questo si comporta come un dict

collections.Counter(y)[0]
> 8

13

Se sai esattamente quale numero stai cercando, puoi utilizzare quanto segue;

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

restituisce quante volte si è verificato 2 nell'array.


8

Sinceramente trovo più semplice convertire in una serie Panda o 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()

O questo bel one-liner proposto da Robert Muil:

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

4
Solo una nota: non è necessario il DataFrame o numpy, può passare direttamente da un elenco a una serie: pd.Series([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]).value_counts()
Robert Muil

Fantastico, è una bella fodera. Big up
wordsforthewise

8

Nessuno ha suggerito di utilizzare numpy.bincount(input, minlength)con minlength = np.size(input), ma sembra essere una soluzione buona, e sicuramente il più veloce :

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

Questo è un folle speedup tra numpy.unique(x, return_counts=True)e numpy.bincount(x, minlength=np.max(x))!


come si confronta con l'istogramma?
john ktejik,

@johnktejik np.histogramnon calcola la stessa cosa. Inutile confrontare i tre approcci che propongo con la histogramfunzione, scusa.
Næreen,

1
@ Næreen bincountfunziona solo per numeri interi, quindi funziona per il problema dell'OP, ma forse non per il problema generico descritto nel titolo. Hai anche provato a utilizzare bincountarray con ints molto grandi?
Imperishable Night,

@ImperishableNight no Non ho provato con ints di grandi dimensioni, ma chiunque è
invitato

Grazie per questo trucco sottovalutato! Sulla mia macchina bincountè circa quattro volte più veloce di unique.
Björn Lindqvist,

6

Che dire di len(y[y==0])e len(y[y==1])?


6

y.tolist().count(val)

con val 0 o 1

Poiché un elenco Python ha una funzione nativa count, la conversione in elenco prima di utilizzare quella funzione è una soluzione semplice.


5

Un'altra soluzione semplice potrebbe essere quella di utilizzare 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

Non lasciare che il nome ti induca in errore, se lo usi con il booleano proprio come nell'esempio, farà il trucco.


5

Per contare il numero di occorrenze, è possibile utilizzare 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

Userei np.where:

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

3

sfruttare i metodi offerti da una serie:

>>> 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

Una risposta generale e semplice sarebbe:

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

che si tradurrebbe in questo codice completo come esempio

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

Ora, se MyArray è in più dimensioni e si desidera contare la ricorrenza di una distribuzione di valori in linea (= modello di seguito)

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

È possibile utilizzare la comprensione del dizionario per creare un one-liner ordinato. Maggiori informazioni sulla comprensione del dizionario sono disponibili qui

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

Questo creerà un dizionario con i valori nel tuo ndarray come chiavi e i conteggi dei valori come rispettivamente i valori delle chiavi.

Funzionerà ogni volta che vuoi contare le occorrenze di un valore nelle matrici di questo formato.


2

Prova questo:

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

1

Questo può essere fatto facilmente nel seguente metodo

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

1

Poiché ndarray contiene solo 0 e 1, è possibile utilizzare sum () per ottenere l'occorrenza di 1s e len () - sum () per ottenere l'occorrenza di 0s.

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

1

Hai un array speciale con solo 1 e 0 qui. Quindi un trucco è usare

np.mean(x)

che ti dà la percentuale di 1s nel tuo array. In alternativa, utilizzare

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

ti darà il numero assoluto di 1 e 0 nel tuo array.


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

Ho appena copiato il commento di Seppo Enarvi che merita di essere una risposta adeguata


0

Implica un ulteriore passaggio, ma una soluzione più flessibile che funzionerebbe anche per array 2D e filtri più complicati consiste nel creare una maschera booleana e quindi utilizzare .sum () sulla maschera.

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

0

Se non vuoi usare numpy o un modulo collezioni puoi usare un dizionario:

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

risultato:

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

Ovviamente puoi anche usare un'istruzione if / else. Penso che la funzione Counter faccia quasi la stessa cosa ma è più trasparente.


0

Per voci generiche:

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)}

Emetterà un conteggio:

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

E indici:

{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

qui ho qualcosa attraverso il quale puoi contare il numero di occorrenze di un determinato numero: secondo il tuo codice

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

stampa (count_of_zero)

// in base alla corrispondenza ci saranno valori booleani e in base al valore Vero verrà restituito il numero 0


0

Se sei interessato all'esecuzione più veloce, sai in anticipo quale / i valore / i cercare e l'array è 1D, oppure sei interessato al risultato sull'array appiattito (nel qual caso l'input della funzione dovrebbe essere np.flatten(arr)piuttosto che semplicemente arr), Numba è tuo amico:

import numba as nb


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

oppure, per array molto grandi in cui la parallelizzazione può essere utile:

@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

Confrontandoli con il confronto np.count_nonzero()(che ha anche un problema di creazione di un array temporaneo che può essere evitato) e una np.unique()soluzione basata

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 

per input generato con:

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

si ottengono i seguenti grafici (la seconda riga di grafici è uno zoom sull'approccio più veloce):

bm_full bm_zoom

Mostrando che le soluzioni basate su Numba sono notevolmente più veloci delle controparti NumPy e, per input molto grandi, l'approccio parallelo è più veloce di quello ingenuo.


Codice completo disponibile qui .


0

se hai a che fare con array molto grandi che utilizzano generatori potrebbe essere un'opzione. La cosa bella qui è che questo approccio funziona bene sia per gli array che per gli elenchi e non è necessario alcun pacchetto aggiuntivo. Inoltre, non stai utilizzando tanta memoria.

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 ha un modulo per questo. Solo un piccolo trucco. Metti il ​​tuo array di input come bin.

numpy.histogram(y, bins=y)

L'output è di 2 array. Uno con i valori stessi, l'altro con le frequenze corrispondenti.


'bin' non dovrebbe essere un numero?
john ktejik,

1
Sì @johnktejik hai ragione. Questa risposta non funziona.
Næreen,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.