web-dev-qa-db-fra.com

numpy: comptes de fréquence les plus efficaces pour des valeurs uniques dans un tableau

Dansnumpy/scipy, existe-t-il un moyen efficace d'obtenir des comptes de fréquence pour des valeurs uniques dans un tableau?

Quelque chose dans ce sens:

x = array( [1,1,1,2,2,2,5,25,1,1] )
y = freq_count( x )
print y

>> [[1, 5], [2,3], [5,1], [25,1]]

(Pour vous, utilisateurs R, je recherche la fonction table())

162
Abe

Regardez np.bincount:

http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html

import numpy as np
x = np.array([1,1,1,2,2,2,5,25,1,1])
y = np.bincount(x)
ii = np.nonzero(y)[0]

Et alors:

Zip(ii,y[ii]) 
# [(1, 5), (2, 3), (5, 1), (25, 1)]

ou:

np.vstack((ii,y[ii])).T
# array([[ 1,  5],
         [ 2,  3],
         [ 5,  1],
         [25,  1]])

ou comme vous voulez combiner les nombres et les valeurs uniques.

118
JoshAdel

Depuis Numpy 1.9, la méthode la plus simple et la plus rapide consiste à utiliser simplement numpy.unique , qui possède désormais un argument de mot clé return_counts:

import numpy as np

x = np.array([1,1,1,2,2,2,5,25,1,1])
unique, counts = np.unique(x, return_counts=True)

print np.asarray((unique, counts)).T

Qui donne:

 [[ 1  5]
  [ 2  3]
  [ 5  1]
  [25  1]]

Une comparaison rapide avec scipy.stats.itemfreq:

In [4]: x = np.random.random_integers(0,100,1e6)

In [5]: %timeit unique, counts = np.unique(x, return_counts=True)
10 loops, best of 3: 31.5 ms per loop

In [6]: %timeit scipy.stats.itemfreq(x)
10 loops, best of 3: 170 ms per loop
360
jme

Mise à jour: la méthode mentionnée dans la réponse d'origine est obsolète, nous devrions utiliser la nouvelle méthode à la place:

>>> import numpy as np
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> np.array(np.unique(x, return_counts=True)).T
    array([[ 1,  5],
           [ 2,  3],
           [ 5,  1],
           [25,  1]])

Réponse originale:

vous pouvez utiliser scipy.stats.itemfreq

>>> from scipy.stats import itemfreq
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> itemfreq(x)
/usr/local/bin/python:1: DeprecationWarning: `itemfreq` is deprecated! `itemfreq` is deprecated and will be removed in a future version. Use instead `np.unique(..., return_counts=True)`
array([[  1.,   5.],
       [  2.,   3.],
       [  5.,   1.],
       [ 25.,   1.]])
107
McKelvin

Cela m’intéressait aussi, j’ai donc fait une petite comparaison de performances (avec perfplot , un de mes projets pour animaux de compagnie). Résultat:

 y = np.bincount(a)
 ii = np.nonzero(y)[0]
 out = np.vstack((ii, y[ii])).T

est de loin le plus rapide. (Notez le log-scaling.)

 enter image description here


Code pour générer l'intrigue:

import numpy as np
import pandas as pd
import perfplot
from scipy.stats import itemfreq


def bincount(a):
    y = np.bincount(a)
    ii = np.nonzero(y)[0]
    return np.vstack((ii, y[ii])).T


def unique(a):
    unique, counts = np.unique(a, return_counts=True)
    return np.asarray((unique, counts)).T


def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack((unique, count)).T


def pandas_value_counts(a):
    out = pd.value_counts(pd.Series(a))
    out.sort_index(inplace=True)
    out = np.stack([out.keys().values, out.values]).T
    return out


perfplot.show(
    setup=lambda n: np.random.randint(0, 1000, n),
    kernels=[bincount, unique, itemfreq, unique_count, pandas_value_counts],
    n_range=[2**k for k in range(26)],
    logx=True,
    logy=True,
    xlabel='len(a)'
    )
27
Nico Schlömer

Utilisation du module pandas:

>>> import pandas as pd
>>> import numpy as np
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> pd.value_counts(pd.Series(x))
1     5
2     3
25    1
5     1

dtype: int64

21
ivankeller

C’est de loin la solution la plus générale et la plus performante; surpris, il n'a pas encore été posté.

import numpy as np

def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack(( unique, count)).T

print unique_count(np.random.randint(-10,10,100))

Contrairement à la réponse actuellement acceptée, il fonctionne sur tous les types de données pouvant être triés (pas uniquement les éléments positifs) et offre des performances optimales. la seule dépense importante concerne le tri effectué par np.unique.

17
Eelco Hoogendoorn

numpy.bincount est probablement le meilleur choix. Si votre tableau contient autre chose que de petits entiers denses, il peut être utile de l'envelopper:

def count_unique(keys):
    uniq_keys = np.unique(keys)
    bins = uniq_keys.searchsorted(keys)
    return uniq_keys, np.bincount(bins)

Par exemple:

>>> x = array([1,1,1,2,2,2,5,25,1,1])
>>> count_unique(x)
(array([ 1,  2,  5, 25]), array([5, 3, 1, 1]))
14
Bi Rico

Même s'il a déjà reçu une réponse, je suggère une approche différente qui utilise numpy.histogram. Cette fonction étant donnée une séquence, elle retourne la fréquence de ses éléments regroupés dans des bacs .

Attention cependant : cela fonctionne dans cet exemple car les nombres sont des entiers. S'ils étaient des nombres réels, cette solution ne s'appliquerait pas aussi bien.

>>> from numpy import histogram
>>> y = histogram (x, bins=x.max()-1)
>>> y
(array([5, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1]),
 array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,  22.,
        23.,  24.,  25.]))
7
Jir
import pandas as pd
import numpy as np
x = np.array( [1,1,1,2,2,2,5,25,1,1] )
print(dict(pd.Series(x).value_counts()))

Cela vous donne: {1: 5, 2: 3, 5: 1, 25: 1}

4
Kerem T

Ancienne question, mais je voudrais proposer ma propre solution, qui s’avère être la plus rapide, utilise normallistau lieu de np.array en tant qu’entrée (ou transfert vers une liste en premier), en fonction de mon test.

Vérifiez-le si vous le rencontrez aussi.

def count(a):
    results = {}
    for x in a:
        if x not in results:
            results[x] = 1
        else:
            results[x] += 1
    return results

Par exemple, 

>>>timeit count([1,1,1,2,2,2,5,25,1,1]) would return:

100 000 boucles, le meilleur des 3: 2,26 µs par boucle

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))

100 000 boucles, le meilleur des 3: 8,8 µs par boucle

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())

100 000 boucles, le meilleur des 3: 5,85 µs par boucle

Alors que la réponse acceptée serait plus lente, la solution scipy.stats.itemfreq est encore pire.


Des tests plus approfondis ne confirmaient pas les attentes formulées.

from zmq import Stopwatch
aZmqSTOPWATCH = Stopwatch()

aDataSETasARRAY = ( 100 * abs( np.random.randn( 150000 ) ) ).astype( np.int )
aDataSETasLIST  = aDataSETasARRAY.tolist()

import numba
@numba.jit
def numba_bincount( anObject ):
    np.bincount(    anObject )
    return

aZmqSTOPWATCH.start();np.bincount(    aDataSETasARRAY );aZmqSTOPWATCH.stop()
14328L

aZmqSTOPWATCH.start();numba_bincount( aDataSETasARRAY );aZmqSTOPWATCH.stop()
592L

aZmqSTOPWATCH.start();count(          aDataSETasLIST  );aZmqSTOPWATCH.stop()
148609L

Réf. commentaires ci-dessous sur le cache et d’autres effets secondaires dans la RAM qui ont une incidence sur les résultats de tests extrêmement répétitifs.

4
Rain Lee

Pour compter uniques non-entiers - similaires à la réponse d'Eelco Hoogendoorn mais considérablement plus rapide (facteur 5 sur ma machine), j'ai utilisé weave.inline pour combiner numpy.unique avec un peu de c-code; 

import numpy as np
from scipy import weave

def count_unique(datain):
  """
  Similar to numpy.unique function for returning unique members of
  data, but also returns their counts
  """
  data = np.sort(datain)
  uniq = np.unique(data)
  nums = np.zeros(uniq.shape, dtype='int')

  code="""
  int i,count,j;
  j=0;
  count=0;
  for(i=1; i<Ndata[0]; i++){
      count++;
      if(data(i) > data(i-1)){
          nums(j) = count;
          count = 0;
          j++;
      }
  }
  // Handle last value
  nums(j) = count+1;
  """
  weave.inline(code,
      ['data', 'nums'],
      extra_compile_args=['-O2'],
      type_converters=weave.converters.blitz)
  return uniq, nums

Informations de profil

> %timeit count_unique(data)
> 10000 loops, best of 3: 55.1 µs per loop

La version numpy pure d'Eelco: 

> %timeit unique_count(data)
> 1000 loops, best of 3: 284 µs per loop

Remarque

Il y a redondance ici (unique effectue également un tri), ce qui signifie que le code pourrait probablement être optimisé davantage en plaçant la fonctionnalité unique dans la boucle de code c. 

2
jmetz

quelque chose comme ça devrait le faire:

#create 100 random numbers
arr = numpy.random.random_integers(0,50,100)

#create a dictionary of the unique values
d = dict([(i,0) for i in numpy.unique(arr)])
for number in arr:
    d[j]+=1   #increment when that value is found

En outre, cet article précédent sur Compter efficacement des éléments uniques semble assez similaire à votre question, à moins que quelque chose me manque.

1
benjaminmgross

compte de fréquence multidimensionnel, i.n. compter les tableaux.

>>> print(color_array    )
  array([[255, 128, 128],
   [255, 128, 128],
   [255, 128, 128],
   ...,
   [255, 128, 128],
   [255, 128, 128],
   [255, 128, 128]], dtype=uint8)


>>> np.unique(color_array,return_counts=True,axis=0)
  (array([[ 60, 151, 161],
    [ 60, 155, 162],
    [ 60, 159, 163],
    [ 61, 143, 162],
    [ 61, 147, 162],
    [ 61, 162, 163],
    [ 62, 166, 164],
    [ 63, 137, 162],
    [ 63, 169, 164],
   array([     1,      2,      2,      1,      4,      1,      1,      2,
         3,      1,      1,      1,      2,      5,      2,      2,
       898,      1,      1,  
0
vishal

importer des pandas en tant que pd

importer numpy en tant que np

pd.Series (name_of_array) .value_counts ()

0
RAJAT BHATHEJA