Нашли ошибку?
продвинутыйNumPy 2.5.3

Маскированные массивы

Массив с пометкой «значения нет» для отдельных элементов — пропуски даже в целых числах.

Нужно знать:NaN и бесконечностиБулевы маски

Содержание статьи

Коротко

  • Модуль np.ma: массив данных плюс булева маска; True в маске — «значения нет».
  • Агрегации (mean, sum, count…) и арифметика пропускают маскированные элементы, маска переносится в результат.
  • Создание: masked_array(data, mask), masked_where(cond, data), masked_equal(data, value), masked_invalid (NaN и inf).
  • Обратно в обычный массив: filled(значение) или compressed() — только известные.
  • Плюс перед NaN: работает с целыми и не требует смены типа. Минус: медленнее и поддерживается не всеми библиотеками.

Синтаксис

m = np.ma.masked_array(data, mask=cond)
m = np.ma.masked_where(cond, data)
m = np.ma.masked_equal(data, -1)
m = np.ma.masked_invalid(data)     # NaN и inf
m.mean(), m.count()
m.filled(0), m.compressed()

Примеры

Данные для примеровиспользуются во всех примерах ниже
# количество заказов по дням; -1 — магазин закрыт, данных нет
orders = np.array([12, 15, -1, 9, -1, 20])
Маскированный массив
m = np.ma.masked_array(orders, mask=orders < 0)
m
Вывод
masked_array(data=[12, 15, --, 9, --, 20],
             mask=[False, False,  True, False,  True, False],
       fill_value=999999)
Агрегации без пропусков
m = np.ma.masked_equal(orders, -1)
print(m.mean(), m.sum(), m.count())     # маскированные элементы не учитываются
orders.mean()                           # обычный массив учитывает −1
Вывод
14.0 56 4
np.float64(9.0)
Маскировать NaN и inf
temps = np.array([12.5, np.nan, 14.0, np.inf])
np.ma.masked_invalid(temps).mean()      # маскирует NaN и inf
Вывод
np.float64(13.25)
Арифметика сохраняет маску
m = np.ma.masked_equal(orders, -1)
m * 10                                  # маска сохраняется
Вывод
masked_array(data=[120, 150, --, 90, --, 200],
             mask=[False, False,  True, False,  True, False],
       fill_value=-1)
Обратно в обычный массив
m = np.ma.masked_equal(orders, -1)
print(m.filled(0))                      # обычный массив, пропуски — 0
m.compressed()                          # только известные значения
Вывод
[12 15  0  9  0 20]
array([12, 15,  9, 20])

Подводные камни

Функции вне np.ma не знают о маске

Многие функции NumPy и других библиотек работают с данными под маской как с обычными значениями. Используйте функции из np.ma (np.ma.median, np.ma.concatenate…) или сначала filled / compressed.

np.concatenate теряет маску
m = np.ma.masked_equal(orders, -1)
print(np.concatenate([m, m]).mean())     # маска потеряна: −1 участвуют в среднем
np.ma.concatenate([m, m]).mean()
Вывод
9.0
np.float64(14.0)

Когда нужна маска, а не NaN

Замена пропусков на NaN превращает целые в float. Маска сохраняет исходный тип.

Тип данных маскированного массива
m = np.ma.masked_equal(orders, -1)
m.dtype                                 # тип остался целым — в отличие от замены на NaN
Вывод
dtype('int64')