2015-06-16 17 views
8

Ho una matrice di forma (128, 36, 8) e mi piacerebbe trovare il numero di occorrenze dei sottotitoli unici di lunghezza 8 nell'ultima dimensione.Conta in modo efficiente il numero di occorrenze di sottotitoli unici in NumPy?

Sono a conoscenza di np.unique e np.bincount, ma quelli sembrano essere per elementi anziché subarray. Ho visto this question ma si tratta di trovare la prima occorrenza di un particolare sottoarray, piuttosto che i conteggi di tutti i sottotitoli unici.

+0

Non riuscivo a trovare un modo per farlo in modo disordinato, ma un [trie] (https://en.wikipedia.org/wiki/Trie) sarebbe troppo lento? Avrebbe solo bisogno di accedere a ciascun elemento una sola volta e alla fine si avrà automaticamente il numero di sottoarray unici e le loro posizioni se li hai memorizzati. – KobeJohn

+0

Ecco una domanda strettamente correlata, http://stackoverflow.com/questions/8560440/removing-duplicate-columns-and-rows-from-a-numpy-2d-array. L'idea di base è di ordinare i sottotitoli (ordinamento lessicografico). Una volta raggruppati i sottoarray simili, è banale identificarli e contarli. –

risposta

3

La questione afferma che la matrice di ingresso è di forma (128, 36, 8) e siamo interessati a trovare sottoarray unici di lunghezza 8 in ultima dimensione. Quindi, presumo che l'unicità sia unita alle prime due dimensioni. Supponiamo che A sia l'array 3D di input.

ottenere il numero di sottoarray unici

# Reshape the 3D array to a 2D array merging the first two dimensions 
Ar = A.reshape(-1,A.shape[2]) 

# Perform lex sort and get the sorted indices and xy pairs 
sorted_idx = np.lexsort(Ar.T) 
sorted_Ar = Ar[sorted_idx,:] 

# Get the count of rows that have at least one TRUE value 
# indicating presence of unique subarray there 
unq_out = np.any(np.diff(sorted_Ar,axis=0),1).sum()+1 

Campione Run -

In [159]: A # A is (2,2,3) 
Out[159]: 
array([[[0, 0, 0], 
     [0, 0, 2]], 

     [[0, 0, 2], 
     [2, 0, 1]]]) 

In [160]: unq_out 
Out[160]: 3 

ottenere il conteggio di occorrenze di sottoarray unici Run

# Reshape the 3D array to a 2D array merging the first two dimensions 
Ar = A.reshape(-1,A.shape[2]) 

# Perform lex sort and get the sorted indices and xy pairs 
sorted_idx = np.lexsort(Ar.T) 
sorted_Ar = Ar[sorted_idx,:] 

# Get IDs for each element based on their uniqueness 
id = np.append([0],np.any(np.diff(sorted_Ar,axis=0),1).cumsum()) 

# Get counts for each ID as the final output 
unq_count = np.bincount(id) 

Esempio -

In [64]: A 
Out[64]: 
array([[[0, 0, 2], 
     [1, 1, 1]], 

     [[1, 1, 1], 
     [1, 2, 0]]]) 

In [65]: unq_count 
Out[65]: array([1, 2, 1], dtype=int64) 
+0

Questo è fantastico: non avevo pensato di usare 'np.lexsort' e non sapevo di' np.diff', ma in realtà siamo interessati a trovare il * numero di occorrenze * di sottoarray unici, non solo il numero di sottotitoli unici. Questo metodo può essere adattato per restituire i sottoarray unici insieme ai loro conteggi, come la risposta di @ farhawa? – Will

+0

Fantastico, grazie. A proposito, la mia modifica della tua risposta originale sembra essere leggermente più veloce della tua estensione: ~ 668 μs vs ~ 685 μs. – Will

+0

@Sarà fantastico! Che ne dici di testarlo su un set di dati più grande, qualcosa come '(1000, 1000, 8)', se possibile? – Divakar

0

Non sono sicuro che sia il modo più efficiente per farlo, ma dovrebbe funzionare.

arr = arr.reshape(128*36,8) 
unique_ = [] 
occurence_ = [] 

for sub in arr: 
    if sub.tolist() not in unique_: 
     unique_.append(sub.tolist()) 
     occurence_.append(1) 
    else: 
     occurence_[unique_.index(sub.tolist())]+=1 
for index_,u in unique_: 
    print u,"occurrence: %s"%occurence_[index_] 
+0

Funzionerà ma stavo cercando di evitare le funzioni che usano Python nativo come 'tolist' e' index', che sono costose. Grazie per la risposta però. – Will

+0

Un'ovvia ottimizzazione del metodo, a proposito, sarebbe quella di mantenere i conteggi in un dizionario in cui le chiavi sono tuple dei sottoarray, piuttosto che in un elenco che dobbiamo continuare a cercare con 'unique_.index'. – Will

+1

@Sai, o meglio ancora, usa 'collections.Counter',' counts = Counter (tupla (row) per riga in arr) ':) –

1

Qui ho modificato @ risposta molto utile di Divakar di restituire i conteggi dei sottoarray unici, così come i sottoarray stessi, in modo che l'uscita è la stessa di quella di collections.Counter.most_common():

# Get the array in 2D form. 
arr = arr.reshape(-1, arr.shape[-1]) 

# Lexicographically sort 
sorted_arr = arr[np.lexsort(arr.T), :] 

# Get the indices where a new row appears 
diff_idx = np.where(np.any(np.diff(sorted_arr, axis=0), 1))[0] 

# Get the unique rows 
unique_rows = [sorted_arr[i] for i in diff_idx] + [sorted_arr[-1]] 

# Get the number of occurences of each unique array (the -1 is needed at 
# the beginning, rather than 0, because of fencepost concerns) 
counts = np.diff(
    np.append(np.insert(diff_idx, 0, -1), sorted_arr.shape[0] - 1)) 

# Return the (row, count) pairs sorted by count 
return sorted(zip(unique_rows, counts), key=lambda x: x[1], reverse=True)