python arrays performance numpy

python - numpy: recuentos de frecuencia más eficientes para valores únicos en una matriz



arrays performance (12)

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

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

Algo en esta línea:

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 ustedes, usuarios de R, básicamente busco la función table() )


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

Sin embargo, ten cuidado : funciona en este ejemplo porque los números son enteros. Si fueran números reales, 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.]))


Eche 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 recuentos y los valores únicos.


Esta es de lejos la solución más general y eficiente; sorprendido de que no haya 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 inglés), y tiene un rendimiento óptimo; el único gasto significativo es en la clasificación hecha por np.unique.


Para contar enteros no enteros únicos , similares a la respuesta de Eelco Hoogendoorn pero considerablemente más rápidos (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<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

Información de perfil

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

La versión numpy pura de numpy :

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

Nota

Aquí hay redundancia (el unique realiza una clasificación), lo que significa que el código probablemente podría optimizarse aún más al colocar la funcionalidad unique dentro del ciclo c-loop.


También me interesó esto, así que hice una pequeña comparación de rendimiento (usando perfplot , un proyecto mío favorito). 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).

Código para generar la trama:

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(22)], logx=True, logy=True, xlabel=''len(a)'' )


Una vieja pregunta, pero me gustaría ofrecer mi propia solución, que resulta ser la más rápida, utilizar la list normal en lugar de np.array como entrada (o transferirla a la lista en primer lugar), según mi prueba de banco.

Compruébalo 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 ciclo

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

100000 bucles, lo mejor de 3: 8,8 μs por ciclo

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

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 en profundidad 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. comentarios a continuación sobre el caché y otros efectos secundarios en RAM que influyen en un pequeño conjunto de datos de resultados de pruebas masivamente repetitivos.


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


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 sobre el conteo eficiente de elementos únicos parece bastante similar a tu pregunta, a menos que me falta algo.


puedes usar scipy.stats.itemfreq

>>> from scipy.stats import itemfreq >>> x = [1,1,1,2,2,2,5,25,1,1] >>> itemfreq(x) array([[ 1., 5.], [ 2., 3.], [ 5., 1.], [ 25., 1.]])


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

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


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}