2015-06-21 16 views
5

Per un grande insieme di punti distribuiti casualmente in un reticolo 2D, voglio estrarre in modo efficiente un subarray, che contiene solo gli elementi che, approssimati come indici, sono assegnati a non- valori zero in una matrice binaria 2D separata. Attualmente, il mio script è il seguente:Manipolazione di array veloce basata sull'inclusione di elementi nella matrice binaria

lat_len = 100 # lattice length 
input = np.random.random(size=(1000,2)) * lat_len 
binary_matrix = np.random.choice(2, lat_len * lat_len).reshape(lat_len, -1) 

def landed(input): 
    output = [] 
    input_as_indices = np.floor(input) 
    for i in range(len(input)): 
     if binary_matrix[input_as_indices[i,0], input_as_indices[i,1]] == 1: 
      output.append(input[i]) 
    output = np.asarray(output) 
    return output 

Tuttavia, ho il sospetto che ci sia un modo migliore per farlo. Lo script precedente può richiedere un tempo piuttosto lungo per eseguire 10000 iterazioni.

risposta

4

Sei corretto. Il calcolo di cui sopra, si può essere fatto in modo più efficiente, senza un ciclo for in Python usando advanced numpy indexing,

def landed2(input): 
    idx = np.floor(input).astype(np.int) 
    mask = binary_matrix[idx[:,0], idx[:,1]] == 1 
    return input[mask] 

res1 = landed(input) 
res2 = landed2(input) 
np.testing.assert_allclose(res1, res2) 

questo si traduce in un ~ 150x speed-up.

+0

Incredibile. Puoi spiegare quali modifiche hanno apportato la maggiore differenza in termini di prestazioni, ad esempio l'uso di int? –

+1

La differenza di prestazioni è dovuta al fatto che evitiamo il ciclo for in python e utilizziamo invece l'indicizzazione numpy avanzata (ho aggiunto un link sopra) che è codificato in modo più efficiente in C. La conversione in interi è solo un effetto collaterale, poiché gli indici non possono avere un 'dtype' float e devono essere interi o booleani. – rth

3

Sembra che si possa ottenere un aumento notevole delle prestazioni se si lavora con matrici indicizzate linearmente. Ecco un'implementazione vettorializzare per risolvere il nostro caso, simile a @rth's answer, ma utilizzando l'indicizzazione lineare -

# Get floor-ed indices 
idx = np.floor(input).astype(np.int) 

# Calculate linear indices 
lin_idx = idx[:,0]*lat_len + idx[:,1] 

# Index raveled/flattened version of binary_matrix with lin_idx 
# to extract and form the desired output 
out = input[binary_matrix.ravel()[lin_idx] ==1] 

Così, in breve abbiamo:

out = input[binary_matrix.ravel()[idx[:,0]*lat_len + idx[:,1]] ==1] 

test Runtime -

Questa sezione confronta la approccio proposto in questa soluzione rispetto allo other solution che utilizza l'indicizzazione di colonne.

Caso # 1 (datasizes originale):

In [62]: lat_len = 100 # lattice length 
    ...: input = np.random.random(size=(1000,2)) * lat_len 
    ...: binary_matrix = np.random.choice(2, lat_len * lat_len). 
              reshape(lat_len, -1) 
    ...: 

In [63]: idx = np.floor(input).astype(np.int) 

In [64]: %timeit input[binary_matrix[idx[:,0], idx[:,1]] == 1] 
10000 loops, best of 3: 121 µs per loop 

In [65]: %timeit input[binary_matrix.ravel()[idx[:,0]*lat_len + idx[:,1]] ==1] 
10000 loops, best of 3: 103 µs per loop 

Caso # 2 (datasizes più grande):

In [75]: lat_len = 1000 # lattice length 
    ...: input = np.random.random(size=(100000,2)) * lat_len 
    ...: binary_matrix = np.random.choice(2, lat_len * lat_len). 
              reshape(lat_len, -1) 
    ...: 

In [76]: idx = np.floor(input).astype(np.int) 

In [77]: %timeit input[binary_matrix[idx[:,0], idx[:,1]] == 1] 
100 loops, best of 3: 18.5 ms per loop 

In [78]: %timeit input[binary_matrix.ravel()[idx[:,0]*lat_len + idx[:,1]] ==1] 
100 loops, best of 3: 13.1 ms per loop 

Così, l'incremento delle prestazioni con questa indicizzazione lineare sembra essere su 20% - 30%.

+0

Quando non sei impegnato a rispondere alle domande 'numpy', vieni a trovarci nella chat room di MATLAB :) Ci manchi! http://chat.stackoverflow.com/rooms/81987/matlab – rayryeng

+0

@rayryeng Bel posto per MATLABans! Oops! Non intendevo "Bans", significava più come le persone di MATLAB! Credo che tornerò quando ci saranno più persone :) – Divakar

+0

MATLABIANI :) ok ci vediamo lì alla fine! – rayryeng

Problemi correlati