NumPyJuniorCoding
Как использовать булево индексирование для фильтрации массивов в NumPy?
Булева маска (массив bool той же формы) фильтрует элементы: x[x > 0]. Результат — копия, не view. 2D-маска даёт flat 1D результат; маска по строкам сохраняет 2D-форму. Используйте & | ~ вместо and/or/not.
Как работает булево индексирование
Булево индексирование — фильтрация элементов массива с помощью булевого массива той же формы. NumPy выбирает только те элементы, где маска равна True, и возвращает одномерный массив (даже если исходный был многомерным).
import numpy as np
x = np.array([10, -3, 7, -1, 5, -8, 2])
# Создаём булеву маску
mask = x > 0
print(mask) # [ True False True False True False True]
print(mask.dtype) # bool
# Применяем маску — получаем копию, не view
positive = x[mask]
print(positive) # [10 7 5 2]
print(positive.shape) # (4,) — 1D независимо от формы x
Составные условия
data = np.array([1.5, np.nan, 3.2, np.nan, 0.8, 4.1, -1.0])
# Операторы &, |, ~ (не and, or, not!)
valid = data[~np.isnan(data) & (data > 0)]
print(valid) # [1.5 3.2 0.8 4.1]
# np.where — выбор из двух массивов по маске
clipped = np.where(data > 3, 3.0, data) # cap значений на 3
print(clipped) # [1.5 nan 3. nan 0.8 3. -1. ]
# np.where только с condition → эквивалент np.nonzero → возвращает индексы
indices = np.where(data > 3)
print(indices) # (array([2, 5]),) — tuple из одного массива для 1D
print(data[indices]) # [3.2 4.1]
Булево индексирование на 2D массивах
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
])
# Маска той же формы — результат всегда 1D
mask_2d = matrix > 4
print(matrix[mask_2d]) # [5 6 7 8 9] — flat array
# Маска по строкам (1D маска для 2D массива)
row_mask = matrix[:, 0] > 2 # первый столбец > 2
print(row_mask) # [False True True]
filtered_rows = matrix[row_mask]
print(filtered_rows) # [[4 5 6]
# [7 8 9]] — форма (2, 3) сохранена!
# Маска по столбцам
col_mask = np.array([True, False, True])
print(matrix[:, col_mask]) # столбцы 0 и 2
Изменение значений через булеву маску
temps = np.array([20.0, -999.0, 22.5, -999.0, 18.0])
sensor_error = -999.0
# Замена значений через индексирование (in-place)
temps[temps == sensor_error] = np.nan
print(temps) # [20. nan 22.5 nan 18. ]
# Clip отрицательных значений
losses = np.array([0.5, -0.1, 0.3, -0.2, 0.8])
losses[losses < 0] = 0.0 # ReLU-подобная операция
print(losses) # [0.5 0. 0.3 0. 0.8]
Подводные камни
- Результат — копия, не view:
x[x > 0]создаёт новый массив. Изменение результата не меняетx. Для in-place изменений используйтеx[x > 0] = value(это срабатывает корректно). - & и | вместо and и or: Python-операторы
and/orне работают с массивами — вызываютValueError: The truth value of an array is ambiguous. Используйте побитовые&,|,~и обязательно расставляйте скобки:(a > 0) & (a < 10). - Маска другой формы — ошибка или неожиданный результат: при несовпадении shape NumPy пытается broadcast маску. Если не получается —
IndexError. Всегда проверяйтеmask.shape == arr.shape. - np.nan в условиях:
np.nan > 0возвращаетFalse,np.nan == np.nanтожеFalse. Для проверки NaN используйте толькоnp.isnan(). - Производительность при повторном использовании: вычисление маски — отдельная аллокация. Если одна маска применяется многократно, сохраните её в переменную вместо пересчёта.
- Индексирование типом int vs bool:
arr[[True, False, True]]— булево,arr[[0, 2]]— fancy indexing, результаты эквивалентны, но поведение при broadcast отличается. Не смешивайте явно.
Common mistakes
- Объяснять
boolean indexing filteringтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
boolean indexing filteringс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.