Devuelve la matriz de conteos para cada característica de entrada

Tengo una matriz de tags de enteros y me gustaría determinar la cantidad de cada etiqueta presente y almacenar esos valores en una matriz del mismo tamaño que la entrada. Esto se puede lograr con el siguiente bucle:

def counter(labels): sizes = numpy.zeros(labels.shape) for num in numpy.unique(labels): mask = labels == num sizes[mask] = numpy.count_nonzero(mask) return sizes 

con entrada:

 array = numpy.array([ [0, 1, 2, 3], [0, 1, 1, 3], [3, 1, 3, 1]]) 

counter() devuelve:

 array([[ 2., 5., 1., 4.], [ 2., 5., 5., 4.], [ 4., 5., 4., 5.]]) 

Sin embargo, para arreglos grandes, con muchas tags únicas, 60,000 en mi caso, esto toma una cantidad de tiempo considerable. Este es el primer paso en un algoritmo complejo y no puedo permitirme gastar más de 30 segundos en este paso. ¿Hay una función que ya existe que puede lograr esto? Si no, ¿cómo puedo acelerar el bucle existente?

Enfoque # 1

Aquí hay uno usando np.unique

 _, tags, count = np.unique(labels, return_counts=1, return_inverse=1) sizes = count[tags] 

Enfoque # 2

Con números positivos en las labels , de manera más simple y eficiente con np.bincount

 sizes = np.bincount(labels)[labels] 

Prueba de tiempo de ejecución

La configuración con 60,000 números positivos únicos y dos conjuntos de longitudes de 100,000 y 1000,000 están cronometrados.

Serie 1 :

 In [192]: np.random.seed(0) ...: labels = np.random.randint(0,60000,(100000)) In [193]: %%timeit ...: sizes = np.zeros(labels.shape) ...: for num in np.unique(labels): ...: mask = labels == num ...: sizes[mask] = np.count_nonzero(mask) 1 loop, best of 3: 2.32 s per loop In [194]: %timeit np.bincount(labels)[labels] 1000 loops, best of 3: 376 µs per loop In [195]: 2320/0.376 # Speedup figure Out[195]: 6170.212765957447 

Set # 2:

 In [196]: np.random.seed(0) ...: labels = np.random.randint(0,60000,(1000000)) In [197]: %%timeit ...: sizes = np.zeros(labels.shape) ...: for num in np.unique(labels): ...: mask = labels == num ...: sizes[mask] = np.count_nonzero(mask) 1 loop, best of 3: 43.6 s per loop In [198]: %timeit np.bincount(labels)[labels] 100 loops, best of 3: 5.15 ms per loop In [199]: 43600/5.15 # Speedup figure Out[199]: 8466.019417475727