Использование нескольких уровней булевой индексной маски в 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
хотя это немного дольше, он, кажется, читается лучше (для моих начинающих глаз), и в тестах скорости он выполняет в то же время.