numpy: la frecuencia más eficiente cuenta para valores únicos en una matriz

En numpy / scipy , ¿existe una forma eficiente de obtener recuentos de frecuencia para valores únicos en una matriz?

Algo a lo largo de estas líneas:

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

(Para usted, usuarios de R, estoy buscando la función table() )

Echa un vistazo a 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] 

Y entonces:

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

o:

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

o como quiera combinar los conteos y los valores únicos.

A partir de Numpy 1.9, el método más fácil y rápido es simplemente usar numpy.unique , que ahora tiene un argumento de palabra clave 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 

Lo que da:

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

Una comparación rápida con 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 

Actualización: el método mencionado en la respuesta original está en desuso, deberíamos usar la nueva forma en su lugar:

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

Respuesta original:

puedes usar 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.]]) 

También me interesó esto, por lo que hice una pequeña comparación de rendimiento (usando perfplot , un proyecto favorito para mí). Resultado:

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

es, con mucho, el más rápido. (Tenga en cuenta la escala de registro.)

introduzca la descripción de la imagen aquí


Código para generar la ttwig:

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

Usando el módulo de 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 

tipo de letra: int64

Esta es, con mucho, la solución más general y de mayor rendimiento. sorprendido no ha sido publicado todavía.

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

A diferencia de la respuesta actualmente aceptada, funciona en cualquier tipo de datos que se pueda ordenar (no solo en caracteres positivos) y tiene un rendimiento óptimo; El único gasto significativo está en la clasificación realizada por np.unique.

numpy.bincount es probablemente la mejor opción. Si su matriz contiene algo más que pequeños enteros densos, podría ser útil envolverlo de esta forma:

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

Por ejemplo:

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

Aunque ya se ha respondido, sugiero un enfoque diferente que hace uso del numpy.histogram . Dicha función, dada una secuencia, devuelve la frecuencia de sus elementos agrupados en contenedores .

Sin embargo, tenga cuidado : funciona en este ejemplo porque los números son enteros. Si fueran números reales, entonces esta solución no se aplicaría tan 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.])) 

Pregunta anterior, pero me gustaría proporcionar mi propia solución que resulte ser la más rápida, usar la list normal en lugar de np.array como entrada (o transferir a la lista en primer lugar), según mi prueba de banco.

Échale un vistazo si lo encuentras también.

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

Por ejemplo,

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

100000 bucles, lo mejor de 3: 2,26 µs por bucle

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

100000 bucles, lo mejor de 3: 8.8 µs por bucle

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

100000 bucles, lo mejor de 3: 5,85 µs por bucle

Mientras que la respuesta aceptada sería más lenta, y la solución scipy.stats.itemfreq es aún peor.


Una prueba más profunda no confirmó la expectativa formulada.

 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 

Árbitro. comente a continuación sobre el caché y otros efectos secundarios en la memoria RAM que influyen en los resultados de pruebas masivas y repetitivas.

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

Esto te da: {1: 5, 2: 3, 5: 1, 25: 1}

Para contar los no enteros únicos : similar a la respuesta de Eelco Hoogendoorn pero considerablemente más rápido (factor de 5 en mi máquina), utilicé weave.inline para combinar numpy.unique con un poco de código c;

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

Información de perfil

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

La versión pura de numpy :

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

Nota

Aquí hay redundancia (el unique realiza una ordenación), lo que significa que el código probablemente podría optimizarse aún más colocando la funcionalidad unique dentro del bucle de código c.

algo como esto debería hacerlo:

 #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 

Además, esta publicación anterior en Contar eficientemente elementos únicos parece bastante similar a tu pregunta, a menos que me esté perdiendo algo.

importar pandas como pd

importar numpy como np

pd.Series (name_of_array) .value_counts ()

desde colecciones import Contador Contador (x)