Замена элементов массива по условию в NumPy. Функции where, place

Создано: 31.12.2025

Для фильтрации массивов в NumPy можно использовать не только булевы маски (см. предыдущий урок), но и функцию where, которая относится к универсальным (ufunc).

import numpy as np

a = np.random.randint(10, 30, size=10)
print(a)

indexes = np.where(a % 3 == 0)
print(indexes, type(indexes))
print(indexes[0])

a = a[indexes]
print(a)
[14 22 19 21 14 15 18 17 27 21]
(array([3, 5, 6, 8, 9]),) <class 'tuple'>
[3 5 6 8 9]
[21 15 18 27 21]

Отличие состоит в том, что where, когда ей передается только логическое выражение, возвращает массив индексов элементов, для которых условие выполняется (массив вложен в кортеж). Далее его можно использовать вместо булевой маски. При этом из исходного экземпляра ndarray значения будут извлекаться по индексам. Другими словами, если в квадратных скобках после имени массива передается структура не из True и False, а из целых чисел, то подразумеваются индексы. Это особенность работы с массивами в NumPy.

a = np.array(['apple', 'banana', 'orange', 'tomato', 'potato'])

i = [1, 3, -1]
print(a[i])  # ['banana' 'tomato' 'potato']

i = np.array([0, 1, 0, 2])
print(a[i])  # ['apple' 'banana' 'apple' 'orange']

print(a[[0, 2, 4]])  # ['apple' 'orange' 'potato']

# Не следует путать с индексацией многомерных массивов:
b = np.array([[1, 2], [3, 4], [5, 6]])
print(b[2,1])  # 6

В связи с тем, что для фильтрации массивов вариант с непосредственным использованием логического выражения в квадратных скобках проще, вызывать функцию where, когда ей передается только один аргумент, имеет смысл, если вам нужны как раз индексы значений, а не они сами. Другими словами, когда выполняется поиск определенных значений.

a = np.array([5, 0, 3, 9, 0, 2, 8, 6])
zeros = np.where(a == 0)
print(zeros)  # (array([1, 4]),)

Или планируется их замена на разные значения:

a[zeros] = [-1, -2]
print(a)  # [ 5 -1  3  9 -2  2  8  6]

Функция where чаще используется не для фильтрации, а для замены элементов в объектах ndarray. При этом ей передается не один аргумент, а три.

a = np.random.normal(10, 10, 10).astype('i2')
print(a)

a = np.where(a < 0, -1, a)
print(a)
[ 7 -4  0  2 23  8 -7 24  1  8]
[ 7 -1  0  2 23  8 -1 24  1  8]

Второй аргумент — это значения, которыми заменяются те элементы в исходном массиве, которые удовлетворяют условию. Третий аргумент — которые нет; в примере выше это будут сами элементы исходного массива, стоящие в соответствующих местах. И второй и третий аргумент могут быть как массивами, так и одиночными значениями. По-сути мы передаем в функцию три массива: булеву маску, чем заменять в ней True, а чем — False.

В NumPy есть особые типы объектов — "не число" (неопределенность) и бесконечности (положительная и отрицательная). Оба имеют тип float. Нередко их используют в статистике, когда данные поступают с ошибкой или по другим причинам. Функция where совместно с функциями isnan, isinf и др. позволяют заменить подобные значения перед применением к массиву тех или иных операций.

a = np.array([6, 3, np.nan, 0, 9, -np.inf])

a = np.where(np.isnan(a) | np.isinf(a), 0, a)
print(a)  # [6. 3. 0. 0. 9. 0.]

В примере прошлого урока мы отфильтровывали выбросы из выборки. Теперь сделаем по-другому. Будем заменять выбросы на границы "усов".

a = np.random.normal(scale=10, size=100).astype('i2')

q1 = np.quantile(a, 0.25)
q3 = np.quantile(a, 0.75)
left_fliers = q1 - 1.5 * (q3 - q1)
right_fliers = q3 + 1.5 * (q3 - q1)

b = np.where(a > right_fliers, int(right_fliers), a)
b = np.where(b < left_fliers, int(left_fliers), b)

Заметим, что, вызывая where, мы постоянно создаем новый массив, выполняя в памяти много лишних действий. Однако в NumPy есть функция place, позволяющая заменять элементы на месте.

np.place(a, a > right_fliers, int(right_fliers))
np.place(a, a < left_fliers, int(left_fliers))

Более того, в NumPy предусмотрен сокращенный синтаксис замены, когда в квадратных скобках стоит условие, а после него присваивание значения:

a[a > right_fliers] = int(right_fliers)
a[a < left_fliers] = int(left_fliers)