2011-08-24 12 views
8

Ho il seguente codice che prima seleziona gli elementi di una matrice NumPy con una maschera indice logico:Utilizzando più livelli di maschera indice booleano NumPy

import numpy as np 

grid = np.random.rand(4,4) 
mask = grid > 0.5 

desidero utilizzare una seconda maschera booleana contro questa per individuare oggetti con:

masklength = len(grid[mask]) 
prob = 0.5 
# generates an random array of bools 
second_mask = np.random.rand(masklength) < prob 

# this fails to act on original object 
grid[mask][second_mask] = 100 

questo non è del tutto lo stesso problema come elencati in questa domanda SO: Numpy array, how to select indices satisfying multiple conditions? - come io sto usando generazione di numeri casuali, non voglio avere per generare una maschera completa, solo per gli elementi selezionati dal prima maschera

risposta

6

Credo che il seguente fa quello che stai chiedendo:

grid[[a[second_mask] for a in np.where(mask)]] = 100 

Funziona come segue:

  • np.where(mask) converte la maschera booleana nelle indici in cui mask è vero;
  • [a[second_mask] for a in ...] sottopone gli indici a selezionare solo quelli in cui è True.

Il motivo per cui la versione originale non funziona è che lo grid[mask] prevede l'indicizzazione. Ciò crea una copia dei dati, che a sua volta produce ...[second_mask] = 100 modificando quella copia anziché l'array originale.

+0

Perfetto, proprio quello che stavo cercando. – Hemmer

+0

Inoltre c'è qualche copia degli array coinvolti nel frammento che hai postato? – Hemmer

+1

@Hemmer: ci sono nuovi array creati da 'np.where' e' a [second_mask] '. La dimensione di questi array dipende dal numero di elementi True in 'mask' e' second_mask' ed è indipendente dalla dimensione di 'grid'. – NPE

0

Un'altra possibile soluzione che ho trovato dopo aver pensato un po 'di più è che la seconda mappa mantenga la dimensione del primo (che può o non valga la pena della memoria) e aggiunge in modo selettivo i nuovi elementi :

#!/usr/bin/env python 
import numpy as np 

prob = 0.5  
grid = np.random.rand(4,4) 

mask = grid > 0.5 
masklength = np.sum(mask) 

# initialise with false map 
second_mask = np.zeros((4,4), dtype=np.bool) 
# then selectively add to this map using the second criteria 
second_mask[mask] = np.random.rand(masklength) < prob 

# this now acts on the original object 
grid[second_mask] = 100 

Anche se questo è un po 'più lungo, sembra di leggere meglio (ai miei occhi principianti), in prove di velocità si effettua nello stesso tempo.

2

Uso indicizzazione piatta evita gran parte della cefalea:

grid.flat[np.flatnonzero(mask)[second_mask]] = 100 

scomponendola:

ind = np.flatnonzero(mask) 

genera una matrice piatta di indici dove mask è vero, che viene poi decimato ulteriormente applicando second_mask :

ind = ind[second_mask] 

Potremmo andare avanti:

ind = ind[third_mask] 

Infine

grid.flat[ind] = 100 

indicizza una versione piatta di grid con ind e assegna 100. grid.ravel()[ind] = 100 funzionerebbe anche, poiché ravel() restituisce una vista piana nell'array originale.

0
In [29]: ar = linspace(1,10,10) 
In [30]: ar[(3<ar)*(ar<8)] 
Out[30]: array([ 4., 5., 6., 7.]) 
Problemi correlati