Замена элементов массива по условию в 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)