Использование нескольких уровней булевой индексной маски в NumPy

у меня есть следующий код, который сначала выбирает элементы массива NumPy с маской логического индекса:

import numpy as np

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

Я хочу использовать вторую логическую маску против этой, Чтобы выбрать объекты с:

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

это не совсем та же проблема, что и в этом вопросе SO: массив Numpy, как выбрать индексы, удовлетворяющие нескольким условиям? - поскольку я использую генерацию случайных чисел, я не хочу генерировать полную маску, только для элементы, выбранные первой маской.

4 ответов


Я считаю, что следующее делает то, что вы просите:

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

он работает следующим образом:

  • np.where(mask) преобразует логическую маску в индексы, где mask - Это верно;
  • [a[second_mask] for a in ...] подмножеств индексов, чтобы выбрать только те, где second_mask - это правда.

причина вашей оригинальной версии не работает что grid[mask] предполагает необычные индексации. Это создает копию данных, что в свою очередь приводит к ...[second_mask] = 100 изменение это копия, а не исходный массив.


использование плоской индексации позволяет избежать большей части головной боли:

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

ломая его:

ind = np.flatnonzero(mask)

генерирует плоский массив индексов, где mask истинно, которое затем уничтожается далее путем применения second_mask:

ind = ind[second_mask] 

мы могли бы пойти на:

ind = ind[third_mask]

наконец-то

grid.flat[ind] = 100

индексирует плоскую версию grid С ind и назначает 100. grid.ravel()[ind] = 100 также будет работать, так как ravel() возвращает плоский вид в исходный массив.


еще одно возможное решение, которое я придумал, подумав об этом немного больше, состоит в том, чтобы вторая карта сохраняла размер первой (которая может или не может стоить попадания в память) и выборочно добавляла новые элементы:

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

хотя это немного дольше, он, кажется, читается лучше (для моих начинающих глаз), и в тестах скорости он выполняет в то же время.


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