Все материалы
Продуктовая аналитикаматериалстарт

NumPy для аналитика: массивы, маски и векторные расчёты

Что нужно знать аналитику о NumPy: массивы, типы, boolean-маски, np.where и векторизация, которая лежит под многими операциями pandas.

КПКейсПрактика5 августа 2026 г.17 мин

Pandas часто скрывает NumPy под привычными операциями над колонками. Но понимание массивов помогает объяснить типы, broadcasting, boolean-маски и причины, по которым векторный расчёт работает быстрее цикла по строкам. Аналитику не нужен весь NumPy: достаточно нескольких идей, которые встречаются каждый день.

Коротко

NumPy хранит однородные числовые массивы и выполняет операции над ними без явного цикла Python. ndarray, маски, np.where, np.select и базовые агрегаты помогают писать расчёты над колонками pandas проще и предсказуемее.

  • Массив обычно содержит значения одного dtype.
  • Операция над массивом применяется к элементам поэлементно.
  • Boolean-маска выбирает нужные значения без цикла.
  • Broadcasting позволяет применять одно число или вектор к массиву совместимой формы.

Массив и dtype

В списке Python числа и строки могут сосуществовать свободно. NumPy старается хранить массив компактно и однородно, поэтому тип влияет на память и результат операции. В аналитике это особенно заметно на больших числовых колонках и при смешении чисел с пропусками.

pythonСоздать массив и выполнить операцию над ним
import numpy as np

revenue = np.array([1200, 800, 450, 1500], dtype='int32')
net_revenue = revenue * 0.9
print(net_revenue)
print(revenue.dtype, revenue.shape)

Маски вместо циклов

Boolean-маска — массив True/False той же длины, что и исходные значения. Она превращает правило фильтрации в объект, который можно проверить и передать дальше. В pandas тот же подход работает для Series и DataFrame.

pythonВыбрать крупные заказы
revenue = np.array([1200, 800, 450, 1500])
large_mask = revenue >= 1000
large_orders = revenue[large_mask]

print(large_mask)       # [ True False False  True ]
print(large_orders)     # [1200 1500]

np.where и np.select для правил

np.where подходит для простого выбора из двух вариантов. Для нескольких условий используй np.select, чтобы порядок правил был виден в коде. В обоих случаях обязательно задай значение для случая, который не попал в условия.

pythonСоздать признак и сегмент заказа
orders['is_large'] = np.where(
    orders['revenue'].ge(1000), True, False
)

conditions = [orders['revenue'].ge(5000), orders['revenue'].ge(1500)]
orders['segment'] = np.select(
    conditions, ['high', 'medium'], default='low'
)

Агрегаты и broadcasting

Агрегаты NumPy считают сумму, среднее и квантили по массиву или выбранной оси. Broadcasting позволяет вычесть среднее из каждого значения без ручного цикла. В реальной метрике всё равно проверь пропуски и знаменатель: NumPy не знает, что означает отсутствие наблюдения.

pythonОтклонение заказа от среднего
revenue = np.array([1200, 800, 450, 1500], dtype='float64')
average = revenue.mean()
deviation = revenue - average

print('mean:', average)
print('p90:', np.quantile(revenue, .9))
print('deviation:', deviation)
NumPy не делает бизнес-правило очевидным автоматически

Векторная операция отвечает за вычисление. Определение периода, пользователя, активного действия и знаменателя остаётся аналитической задачей.

Когда использовать NumPy, а когда оставить pandas

NumPy удобен, когда работаешь с плотным числовым массивом: считаешь преобразование, маску или статистику и точно знаешь форму данных. Pandas добавляет индексы, имена колонок, nullable-типы и операции по группам. Поэтому в продуктовой аналитике обычно читают и описывают данные через pandas, а NumPy используют внутри правила, где он делает код короче или быстрее.

Не превращай DataFrame в values без причины. После этого теряются имена колонок и связь с индексом, а случайное изменение порядка может привести к неверному присваиванию. Если массив нужен для вычисления, возвращай результат обратно в Series с тем же индексом или используй to_numpy только в коротком, явно проверенном участке.

Выбор инструмента должен следовать форме вопроса. groupby, merge, даты и категориальные поля обычно читаются лучше в pandas. Матричные операции, маски над несколькими числовыми рядами и broadcasting естественнее выражаются в NumPy. Граница между ними — часть читаемости pipeline.

Практическая граница между pandas и NumPy
ЗадачаИнструмент первого выбораПочему
прочитать CSV и проверить схемуpandasесть имена колонок и типы таблицы
выбрать строки по условиюpandas или NumPy maskоба варианта сохраняют правило явно
groupby по каналуpandasагрегация привязана к измерению
применить формулу к числовому массивуNumPyоперация векторная и компактная
сложить таблицы по ключуpandasнужно сохранить семантику JOIN

dtype, пропуски и неожиданное приведение

Однородность массива — сила NumPy и источник некоторых сюрпризов. Если в числовой массив добавить строку, NumPy может привести все значения к строковому типу. Если добавить np.nan, целочисленный массив часто станет float. На небольшом примере это незаметно, но на больших данных меняет память и поведение сравнений.

Перед арифметикой проверь dtype, наличие конечных значений и диапазон. np.mean вернёт nan, если в массиве есть пропуск; np.nanmean игнорирует его, но это уже методологическое решение. Игнорировать пропуски можно не всегда: для конверсии отсутствие события может быть нулём, а отсутствие revenue — неизвестным значением.

Для денежных и процентных расчётов отдельно реши, где округлять. Округление каждой строки до сложения и округление итоговой суммы дают разные ответы. NumPy честно выполнит обе операции, но не скажет, какая соответствует договорённости с бизнесом.

pythonПроверить пропуски до агрегирования
values = np.array([1200.0, np.nan, 800.0])
print(values.dtype)
print('mean with NA:', np.mean(values))
print('mean ignoring NA:', np.nanmean(values))

finite = np.isfinite(values)
print('invalid values:', (~finite).sum())

Составные маски нужно читать как условие

Одна маска проста: revenue >= 1000. С несколькими условиями легко ошибиться в приоритетах операторов. Для NumPy используй &, | и ~, а каждое сравнение заключай в скобки. Операторы and и or предназначены для одиночных boolean-значений и не работают как поэлементный выбор массива.

Полезно сохранять маски с именами: is_paid, is_recent, has_valid_user. Тогда итоговое условие можно обсудить с коллегой и проверить долю каждой части. Если итоговая выборка неожиданно пустая, посмотри пересечение условий вместо того, чтобы менять порог наугад.

В продуктовой аналитике маска часто является определением аудитории. Например, «активный пользователь» может означать событие в периоде, исключённое QA-устройство и подтверждённую учётную запись. Унеси это правило из временного массива в документацию метрики, иначе следующий расчёт создаст другую аудиторию.

pythonСобрать и диагностировать аудиторию
is_recent = orders['created_at'].ge('2026-08-01')
is_valid = orders['user_id'].notna()
is_paid = orders['revenue'].gt(0)
audience = is_recent & is_valid & is_paid

print({
    'recent': is_recent.mean(),
    'valid': is_valid.mean(),
    'paid': is_paid.mean(),
    'audience': audience.mean(),
})

Broadcasting: форма массива важнее магии

Broadcasting позволяет применить массив формы (3,) к матрице (2, 3) или одно число ко всем элементам. NumPy сравнивает размеры справа налево: измерения должны совпадать или одно из них должно быть равно единице. Ошибка формы лучше явного цикла, но сообщение может быть непривычным, если размерность не выписана в коде.

Классический сценарий аналитика — нормализовать значения по колонкам или вычесть среднее каждой группы. Для этого часто нужен keepdims=True, чтобы результат сохранил ось и корректно применился обратно. Перед расчётом выведи shape каждого объекта. Это занимает секунду и экономит час отладки.

Не используй broadcasting для скрытой бизнес-логики. Если вектор коэффициентов соответствует каналам, сохрани порядок и названия отдельно. Без индекса NumPy не знает, что третий коэффициент относится к email, а не к referral.

pythonНормализовать строки матрицы
matrix = np.array([[10, 20, 30], [4, 8, 12]], dtype='float64')
row_mean = matrix.mean(axis=1, keepdims=True)
centered = matrix - row_mean

print(matrix.shape, row_mean.shape, centered.shape)
assert centered.shape == matrix.shape

От массива к продуктовой метрике

NumPy ускоряет вычислительную часть, но сама метрика начинается раньше: с определения пользователя, события и периода. Перед тем как вызвать .mean() или np.quantile, ответь, что является наблюдением. Среднее время ответа по событиям не равно среднему времени ответа по пользователям. Массив может быть идеально посчитан и всё равно отвечать не на тот вопрос.

Полезная практика — сначала собрать DataFrame с именованными колонками, сделать проверки и только затем передать числовой массив в вычислительную функцию. После расчёта верни результат в таблицу с ключами сегмента и периода. Так промежуточная оптимизация не разрывает связь между числом и его смыслом.

Для следующего шага возьми одну свою метрику, запиши её зерно, создай маску аудитории, проверь пропуски и сравни векторную формулу с понятной реализацией на маленьком наборе. Если ответы совпали, NumPy стал частью воспроизводимого расчёта, а не отдельным фокусом с массивами.

  • Проверить форму и dtype до операции.
  • Явно решить, что делать с пропусками.
  • Скобками закрепить составные маски.
  • Проверить shape при broadcasting.
  • Вернуть вычисление в таблицу с ключами и понятными названиями.
Продолжить чтение
Вся библиотека