2014-11-18 25 views
7

sto cercando di contare un numero di ogni riga sono riportate in una np.array, ad esempio:contare quante volte ogni riga è presente in numpy.array

import numpy as np 
my_array = np.array([[1, 2, 0, 1, 1, 1], 
        [1, 2, 0, 1, 1, 1], # duplicate of row 0 
        [9, 7, 5, 3, 2, 1], 
        [1, 1, 1, 0, 0, 0], 
        [1, 2, 0, 1, 1, 1], # duplicate of row 0 
        [1, 1, 1, 1, 1, 0]]) 

fila [1, 2, 0, 1, 1, 1] presenta 3 volte.

Una soluzione semplice ingenuo comporterebbe la conversione di tutte le mie righe da tuple, e applicando collections.Counter, in questo modo:

from collections import Counter 
def row_counter(my_array): 
    list_of_tups = [tuple(ele) for ele in my_array] 
    return Counter(list_of_tups) 

che produce:

In [2]: row_counter(my_array) 
Out[2]: Counter({(1, 2, 0, 1, 1, 1): 3, (1, 1, 1, 1, 1, 0): 1, (9, 7, 5, 3, 2, 1): 1, (1, 1, 1, 0, 0, 0): 1}) 

Tuttavia, io sono preoccupato per l'efficienza il mio approccio E forse c'è una libreria che fornisce un modo integrato per farlo. Ho taggato la domanda come pandas perché penso che lo pandas potrebbe avere lo strumento che sto cercando.

+0

Mi piace questo problema! Potresti essere in grado di utilizzare 'np.lexsort' a tuo vantaggio, ma non sono sicuro che la raccolta dopo l'ordinamento possa essere eseguita abbastanza velocemente. – eickenberg

risposta

8

È possibile utilizzare the answer to this other question of yours per ottenere il conteggio degli articoli univoci.

In numpy 1.9 v'è un argomento parola chiave opzionale return_counts, in modo da poter semplicemente fare:

>>> my_array 
array([[1, 2, 0, 1, 1, 1], 
     [1, 2, 0, 1, 1, 1], 
     [9, 7, 5, 3, 2, 1], 
     [1, 1, 1, 0, 0, 0], 
     [1, 2, 0, 1, 1, 1], 
     [1, 1, 1, 1, 1, 0]]) 
>>> dt = np.dtype((np.void, my_array.dtype.itemsize * my_array.shape[1])) 
>>> b = np.ascontiguousarray(my_array).view(dt) 
>>> unq, cnt = np.unique(b, return_counts=True) 
>>> unq = unq.view(my_array.dtype).reshape(-1, my_array.shape[1]) 
>>> unq 
array([[1, 1, 1, 0, 0, 0], 
     [1, 1, 1, 1, 1, 0], 
     [1, 2, 0, 1, 1, 1], 
     [9, 7, 5, 3, 2, 1]]) 
>>> cnt 
array([1, 1, 3, 1]) 

Nelle versioni precedenti, si può fare come:

>>> unq, _ = np.unique(b, return_inverse=True) 
>>> cnt = np.bincount(_) 
>>> unq = unq.view(my_array.dtype).reshape(-1, my_array.shape[1]) 
>>> unq 
array([[1, 1, 1, 0, 0, 0], 
     [1, 1, 1, 1, 1, 0], 
     [1, 2, 0, 1, 1, 1], 
     [9, 7, 5, 3, 2, 1]]) 
>>> cnt 
array([1, 1, 3, 1]) 
+0

L'ultima risagoma può essere semplificata un po 'con: 'unq.view ((my_array.dtype, my_array.shape [1]))'; usa lo stesso tipo di dtype a più voci come prima 'vista'. – hpaulj

2

Un approccio panda potrebbe assomigliare a questo

import pandas as pd 

df = pd.DataFrame(my_array,columns=['c1','c2','c3','c4','c5','c6']) 
df.groupby(['c1','c2','c3','c4','c5','c6']).size() 

Nota: fornire i nomi delle colonne non è necessario

soluzione
+0

non ho idea del perché questo è stato downvoted. Questo è un buon esempio di come farlo usando Pandas. –

+0

Grazie, JD. Lo apprezzo –

+0

Puoi mostrare come lo faresti senza fornire i nomi delle colonne? – Akavall

3

Non è male, ma se la vostra matrice è di grandi dimensioni probabilmente si desidera utilizzare un altro hash efficiente (rispetto a quello predefinito utilizzato da Counter) per le righe prima del conteggio. È possibile farlo con joblib:

A = np.random.rand(5, 10000) 

%timeit (A[:,np.newaxis,:] == A).all(axis=2).sum(axis=1) 
10000 loops, best of 3: 132 µs per loop 

%timeit Counter(joblib.hash(row) for row in A).values() 
1000 loops, best of 3: 1.37 ms per loop 

%timeit Counter(tuple(ele) for ele in A).values() 
100 loops, best of 3: 3.75 ms per loop 

%timeit pd.DataFrame(A).groupby(range(A.shape[1])).size() 
1 loops, best of 3: 2.24 s per loop 

La soluzione panda è estremamente lento (circa 2 s per loop) con questo molte colonne. Per una piccola matrice come quella che hai mostrato il tuo metodo è più veloce di hashing joblib ma più lento di NumPy:

numpy: 100000 loops, best of 3: 15.1 µs per loop 
joblib:1000 loops, best of 3: 885 µs per loop 
tuple: 10000 loops, best of 3: 27 µs per loop 
pandas: 100 loops, best of 3: 2.2 ms per loop 

Se si dispone di un gran numero di righe, allora si può probabilmente trovare un sostituto migliore per Counter per trovare le frequenze di hash .

Modifica: aggiunto parametri di riferimento numpy dalla soluzione di @ acjr nel mio sistema in modo che sia più facile da confrontare. La soluzione numpy è la più veloce in entrambi i casi.

4

(Questo presuppone che la matrice è piuttosto piccola, per esempio meno di 1000 righe.)

Ecco un breve modo NumPy contare quante volte ogni riga appare in un array:

>>> (my_array[:, np.newaxis] == my_array).all(axis=2).sum(axis=1) 
array([3, 3, 1, 1, 3, 1]) 

Questo conta quante volte appare ogni riga in my_array, restituendo un array in cui il primo valore mostra quante volte appare la prima riga, il secondo valore mostra quante volte appare la seconda riga e così via.

+0

Con 'n = np.arange (my_array.shape [0])' si può ottenere un buon risultato anche scrivendo '[n [ui] per ui in (my_array [:, np.newaxis ,:] == mio_array) .all (axis = 2)] '... Bella risposta, l'ho già capito a metà, ma quello che mi lascia perplesso è come esci con la soluzione! – gboffi

0

Una soluzione identica a Jaime di può essere trovato nel numpy_indexed pacchetto (disclaimer: I am its author)

import numpy_indexed as npi 
npi.count(my_array) 
Problemi correlati