2015-08-07 13 views
6

Così, mi è stato navigando StackOverflow per un bel po 'di tempo, ma io non riesco a trovare la soluzione per il mio problemaNumpy: Media dei valori corrispondenti alle posizioni di coordinate uniche

considerare questo

import numpy as np 
coo = np.array([[1, 2], [2, 3], [3, 4], [3, 4], [1, 2], [5, 6], [1, 2]]) 
values = np.array([1, 2, 4, 2, 1, 6, 1]) 

L'array coo contiene le posizioni di coordinate (x, y) x = (1, 2, 3, 3, 1, 5, 1) y = (2, 3, 4, 2, 6, 2)

ei valori rappresentano una sorta di dati per questo punto della griglia.

Ora voglio ottenere la media di tutti i valori per ogni punto della griglia univoco. Ad esempio la coordinata (1, 2) si verifica nelle posizioni (0, 4, 6), quindi per questo punto voglio values[[0, 4, 6]].

Come posso ottenere questo per tutti i punti della griglia univoci?

+1

Qual è il dtype di 'coo'? I valori sono sempre valori non negativi? C'è un valore massimo per le coordinate? – unutbu

+1

il dtype nel mio caso sarebbe "float" e le coordinate possono assumere valori arbitrari, anche negativi – HansSnah

+0

@HansSnah Spero tu non stia provando i controlli di uguaglianza sui float in una vera app. :) –

risposta

3

È possibile ordinare coo con np.lexsort per portare i duplicati in successione. Quindi esegui np.diff lungo le righe per ottenere una maschera di inizio di XY univoci nella versione ordinata. Usando questa maschera, puoi creare una matrice ID che avrebbe lo stesso ID per i duplicati. L'array ID può quindi essere utilizzato con np.bincount per ottenere la somma di tutti i valori con lo stesso ID e anche i loro conteggi e quindi i valori medi, come output finale. Ecco un'implementazione di andare in questo senso -

# Use lexsort to bring duplicate coo XY's in succession 
sortidx = np.lexsort(coo.T) 
sorted_coo = coo[sortidx] 

# Get mask of start of each unique coo XY 
unqID_mask = np.append(True,np.any(np.diff(sorted_coo,axis=0),axis=1)) 

# Tag/ID each coo XY based on their uniqueness among others 
ID = unqID_mask.cumsum()-1 

# Get unique coo XY's 
unq_coo = sorted_coo[unqID_mask] 

# Finally use bincount to get the summation of all coo within same IDs 
# and their counts and thus the average values 
average_values = np.bincount(ID,values[sortidx])/np.bincount(ID) 

Campione Run -

In [65]: coo 
Out[65]: 
array([[1, 2], 
     [2, 3], 
     [3, 4], 
     [3, 4], 
     [1, 2], 
     [5, 6], 
     [1, 2]]) 

In [66]: values 
Out[66]: array([1, 2, 4, 2, 1, 6, 1]) 

In [67]: unq_coo 
Out[67]: 
array([[1, 2], 
     [2, 3], 
     [3, 4], 
     [5, 6]]) 

In [68]: average_values 
Out[68]: array([ 1., 2., 3., 6.]) 
+1

Molto intelligente! Grazie. L'ho già implementato nel mio codice! – HansSnah

+0

È possibile ottenere i conteggi dalle posizioni degli indici del risultato di 'diff', che in pratica è ciò che' np.unique' fa quando chiedi 'return_counts = True', e in genere è più veloce di chiamare' bincount'. – Jaime

+0

@Jaime Ah sì che potrebbe essere usato anche per contare. Non conoscevo i numeri delle prestazioni relativi a questi due approcci, buono a sapersi, grazie! – Divakar

2

È possibile utilizzare where:

>>> values[np.where((coo == [1, 2]).all(1))].mean() 
1.0 
+0

Immagino che funzionerebbe se eseguissi un loop su tutti i valori univoci, ma vorrei evitare che – HansSnah

1

E 'molto probabile che sarà più veloce per appiattire l'indici , ovvero:

flat_index = coo[:, 0] * np.max(coo[:, 1]) + coo[:, 1] 

quindi utilizzare np.unique su di esso:

unq, unq_idx, unq_inv, unq_cnt = np.unique(flat_index, 
              return_index=True, 
              return_inverse=True, 
              return_counts=True) 
unique_coo = coo[unq_idx] 
unique_mean = np.bincount(unq_inv, values)/unq_cnt 

rispetto all'approccio simile usando lexsort.

Ma sotto il cofano il metodo è praticamente lo stesso.

+0

Anche molto pulito, tuttavia ho scoperto che, quando applicato ai miei dati, l'indice flat non è univoco ed i risultati sono leggermente diversi per alcune combinazioni rispetto all'approccio lexsort – HansSnah

+0

Questo probabilmente perché ho incasinato: devi moltiplicare gli indici di riga per l'indice di colonna più grande, non per l'indice di riga più grande. Ho modificato sopra, dovrebbe funzionare bene ora. – Jaime

+0

L'OP ha chiarito [nei commenti] (http://stackoverflow.com/questions/31878240/numpy-average-of-values-corresponding-to-unique-coordinate-positions/31880196#comment51674739_31878240) che i valori di 'coo' possono essere galleggianti. Se 'coo = np.array ([[0, 2], [0.5, 1]]), allora' flat_index' equivale a 'array ([2., 2.])', Combinando quindi due coordinate che non sono lo stesso. – unutbu

1

Questo è un semplice one-liner utilizzando il pacchetto numpy_indexed (disclaimer: io sono il suo autore):

import numpy_indexed as npi 
unique, mean = npi.group_by(coo).mean(values) 

dovrebbe essere paragonabile alla risposta attualmente accettato in termini di prestazioni, come fa cose simili sotto il cofano ; ma tutto in un pacchetto ben testato con una bella interfaccia.

+0

Grazie! Apprezzo il commento, ma non ho testato/implementato la tua routine poiché non voglio mantenere il numero di dipendenze in tutti i miei progetti il ​​più basso possibile. – HansSnah

+0

È sia pip che conda installabili su tutte le piattaforme; ma questa è la tua chiamata. Sentiti libero di copiare incollare anche i bit rilevanti dal mio repo. –

Problemi correlati