Comment compter l'occurrence de certains éléments dans un ndarray en Python?


376

En Python, j'ai un ndarray y qui est imprimé commearray([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

J'essaie de compter combien de 0s et combien il y a de 1s dans ce tableau.

Mais quand je tape y.count(0)ou y.count(1), ça dit

numpy.ndarray l'objet n'a pas d'attribut count

Que devrais-je faire?


8
Ne pouvez-vous pas utiliser la fonction somme et longueur, car vous n'avez que des as et des zéros?
codingEnthusiast

Dans ce cas, il est également possible d'utiliser simplement numpy.count_nonzero.
Mong H. Ng

Réponses:


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}

Manière non numpy :

Utilisation 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
Ce serait `` `` unique, count = numpy.unique (a, return_counts = True) dict (zip (unique, count)) ``
déchiquetage

25
Si vous voulez le dictionnaire,dict(zip(*numpy.unique(a, return_counts=True)))
Seppo Enarvi

2
Que faire si je veux accéder au nombre d'occurrences de chaque élément unique du tableau sans attribuer à la variable - count. Des indices à ce sujet?
sajis997

J'ai le même objectif que @ sajis997. Je veux utiliser 'count' comme fonction d'agrégation dans un groupby
p_sutherland

1
Essayé en utilisant les deux méthodes pour un très grand tableau (~ 30 Go). La méthode Numpy a manqué de mémoire alors que cela collections.Counterfonctionnait très bien
Ivan Novikov

252

Qu'en est-il de l'utilisation numpy.count_nonzero, quelque chose comme

>>> 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
Cette réponse semble meilleure que celle avec le plus de votes positifs.
Alex

1
Je ne pense pas que cela fonctionnerait numpy.ndarraycomme OP l'avait demandé à l'origine.
LYu

5
@LYu - le y est un np.ndarray dans cette réponse. Aussi - la plupart sinon toutes les fonctions de np.something fonctionnent sur les ndarrays sans problème.
mmagnuski

132

Personnellement, je choisirais: (y == 0).sum()et(y == 1).sum()

Par exemple

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
C'est certainement le plus facile à lire. La question est de savoir laquelle est la plus rapide et la plus économe en espace
Nathan

Peut être moins économe en espace que numpy.count_nonzero (y == 0), car il évalue le vecteur (y == 0)
Sridhar Thiagarajan

J'aime ça parce que c'est similaire à matlab / octavesum( vector==value )
ePi272314

39

Pour votre cas, vous pouvez également consulter 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

Ce code peut être l'une des solutions les plus rapides pour les tableaux plus grands que j'ai expérimentés. Obtenir le résultat sous forme de liste est également un bonus. Merci!
Youngsup Kim

Et si 'a' est un tableau à n dimensions, nous pouvons simplement utiliser: np.bincount (np.reshape (a, a.size))
Ari

21

Convertissez votre tableau yen liste l, puis faites l.count(1)etl.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])

Si vous savez qu'ils sont justes 0et 1:

np.sum(y)

vous en donne le nombre. np.sum(1-y)donne les zéros.

Pour une généralité légère, si vous voulez compter 0et non pas zéro (mais éventuellement 2 ou 3):

np.count_nonzero(y)

donne le nombre de non nul.

Mais si vous avez besoin de quelque chose de plus compliqué, je ne pense pas que numpy fournira une bonne countoption. Dans ce cas, accédez aux collections:

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

Cela se comporte comme un dicton

collections.Counter(y)[0]
> 8

13

Si vous savez exactement quel numéro vous recherchez, vous pouvez utiliser ce qui suit;

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

retourne combien de fois 2 s'est produit dans votre tableau.


8

Honnêtement, je trouve qu'il est plus facile de convertir une série 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 ce joli one-liner proposé par Robert Muil:

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

4
Juste une note: pas besoin de DataFrame ou numpy, peut aller directement d'une liste à une série: pd.Series([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]).value_counts()
Robert Muil

Génial, c'est une belle doublure. Big up
wordsforthewise

8

Personne n'a suggéré d'utiliser numpy.bincount(input, minlength)avec minlength = np.size(input), mais cela semble être une bonne solution, et certainement la plus rapide :

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

C'est une accélération folle entre numpy.unique(x, return_counts=True)et numpy.bincount(x, minlength=np.max(x))!


comment comparer avec l'histogramme?
john ktejik

@johnktejik np.histogramne calcule pas la même chose. Inutile de comparer les trois approches que je propose avec la histogramfonction, désolé.
Næreen

1
@ Næreen bincountne fonctionne que pour les entiers, donc cela fonctionne pour le problème de l'OP, mais peut-être pas pour le problème générique décrit dans le titre. Avez-vous également essayé d'utiliser bincountdes tableaux avec de très gros pouces?
Nuit impérissable du

@ImperishableNight non, je n'ai pas essayé avec de gros pouces, mais tout le monde est invité à le faire et à publier son propre benchmark :-)
Næreen

Merci pour cette astuce sous-estimée! Sur ma machine bincountest environ quatre fois plus rapide que unique.
Björn Lindqvist


6

y.tolist().count(val)

avec val 0 ou 1

Étant donné qu'une liste python a une fonction native count, la conversion en liste avant d'utiliser cette fonction est une solution simple.


5

Encore une autre solution simple pourrait être d'utiliser 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

Ne laissez pas le nom vous induire en erreur, si vous l'utilisez avec le booléen comme dans l'exemple, il fera l'affaire.


5

Pour compter le nombre d'occurrences, vous pouvez utiliser 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

J'utiliserais np.where:

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

3

profitez des méthodes offertes par une 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

Une réponse générale et simple serait:

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

qui résulterait en ce code complet comme exemple

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

Maintenant, si MyArray est en plusieurs dimensions et que vous souhaitez compter l'occurrence d'une distribution de valeurs en ligne (= modèle ci-après)

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

Vous pouvez utiliser la compréhension du dictionnaire pour créer une ligne nette soignée. Pour en savoir plus sur la compréhension du dictionnaire, cliquez ici

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

Cela créera un dictionnaire avec les valeurs de votre ndarray sous forme de clés et le nombre de valeurs comme valeurs pour les clés respectivement.

Cela fonctionnera chaque fois que vous souhaitez compter les occurrences d'une valeur dans des tableaux de ce format.


2

Essaye ça:

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

1

Cela peut être fait facilement dans la méthode suivante

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

1

Puisque votre ndarray ne contient que 0 et 1, vous pouvez utiliser sum () pour obtenir l'occurrence de 1 et len ​​() - sum () pour obtenir l'occurrence de 0.

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

1

Vous avez un tableau spécial avec seulement 1 et 0 ici. Donc, une astuce consiste à utiliser

np.mean(x)

ce qui vous donne le pourcentage de 1 dans votre tableau. Vous pouvez également utiliser

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

vous donnera le nombre absolu de 1 et 0 dans votre tableau.


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

Je viens de copier ici le commentaire de Seppo Enarvi qui mérite d'être une bonne réponse


0

Cela implique une étape de plus, mais une solution plus flexible qui fonctionnerait également pour les tableaux 2D et les filtres plus compliqués consiste à créer un masque booléen, puis à utiliser .sum () sur le masque.

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

0

Si vous ne voulez pas utiliser numpy ou un module de collections, vous pouvez utiliser un dictionnaire:

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

résultat:

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

Bien sûr, vous pouvez également utiliser une instruction if / else. Je pense que la fonction Counter fait presque la même chose mais c'est plus transparent.


0

Pour les entrées génériques:

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

Sortira un décompte:

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

Et les indices:

{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

ici j'ai quelque chose, à travers lequel vous pouvez compter le nombre d'occurrence d'un nombre particulier: selon votre code

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

imprimer (count_of_zero)

// selon la correspondance, il y aura des valeurs booléennes et selon la valeur True, le nombre 0 sera retourné


0

Si vous êtes intéressé par l'exécution la plus rapide, vous savez à l'avance quelle (s) valeur (s) rechercher, et votre tableau est 1D, ou vous êtes autrement intéressé par le résultat sur le tableau aplati (auquel cas l'entrée de la fonction devrait être np.flatten(arr)plutôt que juste arr), alors Numba est votre ami:

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, pour les très grands tableaux où la parallélisation peut être bénéfique:

@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

Analyse comparative de ceux-ci np.count_nonzero()(qui a également un problème de création d'un tableau temporaire qui peut être évité) et d'une np.unique()solution basée sur

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 

pour les entrées générées avec:

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

les tracés suivants sont obtenus (la deuxième rangée de tracés est un zoom sur l'approche la plus rapide):

bm_full bm_zoom

Montrant que la solution basée sur Numba est sensiblement plus rapide que ses homologues NumPy, et, pour de très grandes entrées, l'approche parallèle est plus rapide que la naïve.


Code complet disponible ici .


0

si vous avez affaire à de très grands tableaux utilisant des générateurs, cela pourrait être une option. La bonne chose ici, c'est que cette approche fonctionne bien pour les tableaux et les listes et vous n'avez pas besoin de package supplémentaire. En outre, vous n'utilisez pas autant de mémoire.

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 a un module pour cela. Juste un petit hack. Mettez votre tableau d'entrée sous forme de bacs.

numpy.histogram(y, bins=y)

La sortie est constituée de 2 tableaux. L'un avec les valeurs elles-mêmes, l'autre avec les fréquences correspondantes.


les «bacs» ne sont-ils pas censés être un nombre?
john ktejik

1
Oui @johnktejik, vous avez raison. Cette réponse ne fonctionne pas .
Næreen

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.