NumPy: массивы, форма и индексация

Продвинутая индексация: булевы маски и целочисленные массивы индексов

Содержание курса

Отбор элементов: булева маска и целочисленные индексы

Маска готова — теперь её можно использовать как индекс. Синтаксис arr[mask] говорит NumPy: «верни мне те элементы arr, где маска равна True».

import numpy as np

arr = np.array([10, 20, 30, 40, 50])
mask = arr >= 30
print(mask)          # [False False  True  True  True]
result = arr[mask]
print(result)        # [30 40 50]
print(result.shape)  # (3,)

Когда маска имеет ту же форму, что и массив (mask.shape == arr.shape), результат arr[mask] — всегда одномерный массив формы (k,), где k — количество True в маске. Это верно и для двумерного случая:

matrix = np.array([[1, 8, 3], [6, 2, 9]])
mask2d = matrix > 5
print(mask2d)
# [[False  True False]
#  [ True False  True]]
print(matrix[mask2d])        # [8 6 9]
print(matrix[mask2d].shape)  # (3,)

NumPy «разворачивает» матрицу и собирает подходящие элементы в плоский список — структура измерений не сохраняется. Важно: это правило (k,) действует именно тогда, когда маска полностью совпадает по форме с массивом. Если маска применяется только по одной оси, поведение иное — но такие варианты в этом уроке не рассматриваются.

Скомбинированные маски работают точно так же:

print(arr[(arr > 15) & (arr < 45)])  # [20 30 40]
print(arr[~mask])                    # [10 20]

Целочисленная индексация — второй способ продвинутой индексации. Вместо булевой маски передаётся массив (или список) целых чисел — позиций, которые нужно извлечь:

idx = np.array([4, 0, 2])
print(arr[idx])        # [50 10 30]
print(arr[idx].shape)  # (3,)

Для одномерного arr форма результата совпадает с формой массива индексов idx. Если idx имеет shape (3,), результат тоже (3,). Для многомерных массивов неиндексированные оси добавляются к форме результата по другим правилам — это отдельная тема, которая здесь не обобщается.

Два важных свойства целочисленной индексации, которых нет у обычных срезов:

  • индексы можно задавать в произвольном порядке — порядок элементов в результате совпадает с порядком в idx;
  • индексы можно повторять — один и тот же элемент попадёт в результат столько раз, сколько его индекс встречается в idx.
idx_repeat = np.array([0, 0, 3, 1])
print(arr[idx_repeat])  # [10 10 40 20]

Сравним оба способа рядом:

| Способ | Синтаксис | Shape результата (в рамках этого урока) | |---|---|---| | Булева маска | arr[mask] | (k,) — при mask.shape == arr.shape | | Целочисленный массив | arr[idx] | совпадает с idx.shape — для одномерного arr |

Оба способа работают для чтения данных — создают новый массив, не меняя исходный arr. Как использовать ту же конструкцию для записи — разберём в следующем разделе.