2011-08-24 21 views
8

tengo el siguiente código que primero selecciona elementos de una matriz NumPy con una máscara índice lógico:Uso de múltiples niveles de máscara índice booleano en NumPy

import numpy as np 

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

Deseo utilizar una segunda máscara boolean contra éste para seleccionar objetos 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 

esto no es del todo el mismo problema que se señalan en esta cuestión de forma: Numpy array, how to select indices satisfying multiple conditions? - como yo estoy usando generación de números aleatorios, no quiero tener que generar una máscara completa, solo para los elementos seleccionados por primera máscara

Respuesta

6

Creo que el siguiente no lo que preguntas:

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

Consiste en lo siguiente:

  • np.where(mask) convierte la máscara booleana en los índices donde mask es cierto;
  • [a[second_mask] for a in ...] subconjuntos los índices para seleccionar solo aquellos en los que second_mask es verdadero.

El motivo por el que su versión original no funciona es que grid[mask] implica una elegante indexación. Esto crea una copia de los datos, que a su vez da como resultado ...[second_mask] = 100 modificando esa copia en lugar de la matriz original.

+0

Perfecto, justo lo que estaba buscando. – Hemmer

+0

También hay copia de matrices involucradas en el fragmento que publicó? – Hemmer

+1

@Hemmer: hay nuevas matrices creadas por 'np.where' y' a [second_mask] '. El tamaño de esas matrices depende de la cantidad de elementos True en 'mask' y' second_mask' y es independiente del tamaño de 'grid'. – NPE

0

Otra solución posible que surgió después de pensar en esto un poco más es hacer que el segundo mapa conserve el tamaño del primero (que puede o no merecer la pena) y agregar selectivamente los nuevos elementos :

#!/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 

Aunque esto es un poco más largo, parece que leer mejor (a mis ojos para principiantes), y en las pruebas de velocidad se realiza en el mismo tiempo.

2

Uso de indexación plana evita gran parte de la cefalea:

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

Descomponiéndola:

ind = np.flatnonzero(mask) 

genera una matriz plana de índices donde mask es cierto, que luego se diezmó adicionalmente aplicando second_mask :

ind = ind[second_mask] 

Podríamos seguir:

ind = ind[third_mask] 

Finalmente

grid.flat[ind] = 100 

índices una versión plana de grid con ind y asigna 100. grid.ravel()[ind] = 100 también funcionaría, ya que ravel() devuelve una vista plana en la matriz original.

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