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()
)
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.
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
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.]])
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.)
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)'
)
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
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.
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]))
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.]))
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}
Ancienne question, mais je voudrais proposer ma propre solution, qui s’avère être la plus rapide, utilise normallist
au 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.
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.
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.
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,
importer des pandas en tant que pd
importer numpy en tant que np
pd.Series (name_of_array) .value_counts ()