new-lvl.pro · Статьи · Python
Статья // 30 мин практики

Pandas за 30 минут:
Python для
аналитика

Минимально необходимый pandas: загрузка, фильтрация, группировка, merge, pivot. Каждый блок — задача из реальной работы аналитика. Если знаете SQL — будет легко.

Зачем аналитику pandas, если есть SQL

SQL — для получения данных из базы. Pandas — для трансформации, анализа и визуализации после выгрузки. В реальной работе цепочка: SQL-запрос → DataFrame в pandas → анализ → графики → выводы. Без pandas вы застреваете на первом шаге.

SQL
Получение данных
Агрегация в базе, JOINы, фильтрация больших таблиц. Работает на сервере — быстро для миллиардов строк.
SELECT, JOIN, GROUP BY, HAVING
pandas
Анализ и трансформация
Работа с выгрузкой: pivot, мержи, хитрые фильтры, создание новых колонок, визуализация, экспорт.
DataFrame, merge, groupby, pivot_table
// Соглашение в этой статье
Все примеры используют один датасет e-commerce — те же таблицы orders и users, что в SQL-задачах на сайте. Если вы решали SQL-задачи — узнаете данные. Импорт: import pandas as pd — подразумевается в каждом примере.

Загрузка данных

Первый шаг — загрузить данные в DataFrame. Три основных источника: CSV-файл, SQL-запрос к базе, Excel.

Загрузка из CSVPython
import pandas as pd

# Из CSV-файла
orders = pd.read_csv('orders.csv', parse_dates=['order_date'])
users  = pd.read_csv('users.csv',  parse_dates=['registration_date'])

# Первый взгляд на данные
orders.head()          # первые 5 строк
orders.shape           # (строки, столбцы)
orders.dtypes          # типы данных
orders.describe()      # статистика по числовым
orders.isna().sum()   # пропуски по столбцам
Загрузка из SQLPython
import pandas as pd
from sqlalchemy import create_engine

engine = create_engine('postgresql://user:pass@host:5432/db')

# Весь запрос — в pandas
orders = pd.read_sql('''
  SELECT * FROM orders
  WHERE order_date >= '2025-01-01'
''', engine, parse_dates=['order_date'])
// parse_dates — не забывайте
Без parse_dates=['order_date'] даты останутся строками — и вы не сможете фильтровать по месяцам, считать разницу между датами и группировать по периодам. Указывайте всегда.

Фильтрация и выбор данных

В SQL — WHERE. В pandas — булева индексация. Выглядит иначе, но логика та же: задаёте условие, получаете подмножество строк.

SQL
SELECT * FROM orders
WHERE status = 'completed'
  AND amount > 1000;
pandas
mask = (
  (orders['status'] == 'completed') &
  (orders['amount'] > 1000)
)
result = orders[mask]
Частые паттерны фильтрацииPython
# По значениям из списка (аналог IN)
ios_android = users[users['platform'].isin(['ios', 'android'])]

# По дате (аналог WHERE date BETWEEN)
jan = orders[orders['order_date'].between('2025-01-01', '2025-01-31')]

# Исключить NULL (аналог WHERE col IS NOT NULL)
clean = orders[orders['amount'].notna()]

# По подстроке (аналог LIKE '%promo%')
promo = orders[orders['channel'].str.contains('promo', na=False)]

# Выбор столбцов (аналог SELECT col1, col2)
subset = orders[['user_id', 'amount', 'order_date']]
1
Задача: выберите завершённые заказы дороже 500₽ за март 2025
Из таблицы orders отфильтруйте строки, где status = 'completed', amount > 500 и order_date в марте 2025. Выведите только user_id, order_date, amount.
Показать решение
РешениеPython
mask = (
  (orders['status'] == 'completed') &
  (orders['amount'] > 500) &
  (orders['order_date'] >= '2025-03-01') &
  (orders['order_date'] < '2025-04-01')
)
result = orders.loc[mask, ['user_id', 'order_date', 'amount']]
.loc[mask, columns] — одновременно фильтрует строки и выбирает столбцы. Чище, чем orders[mask][cols].

Группировка и агрегация

groupby в pandas — это GROUP BY в SQL. Группируете по одному или нескольким столбцам, потом применяете агрегатную функцию: sum, mean, count, nunique.

SQL
SELECT
  DATE_TRUNC('month', order_date) AS m,
  COUNT(*) AS orders,
  SUM(amount) AS revenue
FROM orders
GROUP BY 1;
pandas
monthly = (orders
  .groupby(orders['order_date'].dt.to_period('M'))
  .agg(
    orders=('order_id', 'count'),
    revenue=('amount', 'sum'),
  )
)
Частые паттерны группировкиPython
# Несколько агрегатов с именованием (pandas >= 0.25)
stats = (orders
  .groupby('platform')
  .agg(
    total_revenue = ('amount', 'sum'),
    avg_check     = ('amount', 'mean'),
    unique_buyers = ('user_id', 'nunique'),
    order_count   = ('order_id', 'count'),
  )
  .round(0)
)

# Группировка по нескольким колонкам
by_month_platform = (orders
  .assign(month=orders['order_date'].dt.to_period('M'))
  .groupby(['month', 'platform'])
  .agg(revenue=('amount', 'sum'))
  .reset_index()
)

# Добавить новую колонку (аналог оконной функции)
orders['user_total'] = orders.groupby('user_id')['amount'].transform('sum')
// transform vs agg
agg схлопывает группу в одну строку (как GROUP BY). transform возвращает результат на каждую строку — как оконная функция SUM OVER(PARTITION BY) в SQL. Это ключевое различие.
2
Задача: топ-5 каналов привлечения по выручке
Объедините orders и users по user_id. Посчитайте суммарную выручку (только completed) по каждому acquisition_channel. Выведите топ-5 каналов, отсортированных по убыванию.
Показать решение
РешениеPython
df = orders[orders['status'] == 'completed'].merge(users, on='user_id')

top5 = (df
  .groupby('acquisition_channel')
  .agg(revenue=('amount', 'sum'))
  .sort_values('revenue', ascending=False)
  .head(5)
)
merge → groupby → sort_values → head — типичная цепочка. Обратите внимание: сначала фильтруем completed, потом мержим — так быстрее, чем наоборот.

Merge: объединение таблиц

merge в pandas — это JOIN в SQL. Поддерживает inner, left, right, outer. По умолчанию — inner.

SQL
SELECT o.*, u.platform
FROM orders o
LEFT JOIN users u
  ON o.user_id = u.user_id;
pandas
df = orders.merge(
  users[['user_id', 'platform']],
  on='user_id',
  how='left'
)
Виды mergePython
# inner (по умолчанию) — только совпадения
df = orders.merge(users, on='user_id')

# left — все строки из левой + совпадения из правой
df = orders.merge(users, on='user_id', how='left')

# Разные имена колонок
df = orders.merge(users, left_on='buyer_id', right_on='user_id')

# Проверка: не «раздулся» ли DataFrame после merge
print(len(orders), '→', len(df))  # должно быть ≈ одинаково
// Ловушка: дубликаты при merge
Если в правой таблице несколько строк с одним user_id — merge создаст дубликаты. Всегда проверяйте len(df) после merge. Если DataFrame «раздулся» — в правой таблице дубли. Решение: сначала дедуплицируйте правую таблицу или агрегируйте.
3
Задача: LTV по каналам привлечения за 6 месяцев
Объедините orders и users. Оставьте только заказы в первые 6 месяцев после регистрации. Посчитайте средний LTV (сумма заказов / количество уникальных пользователей) по каждому acquisition_channel.
Показать решение
РешениеPython
df = orders.merge(users, on='user_id')

# Только первые 6 месяцев
df['months_since'] = (
  (df['order_date'] - df['registration_date']).dt.days / 30
)
df_6m = df[(df['months_since'] >= 0) & (df['months_since'] < 6)]

ltv = (df_6m
  .groupby('acquisition_channel')
  .agg(
    total_rev = ('amount', 'sum'),
    users     = ('user_id', 'nunique'),
  )
)
ltv['ltv_6m'] = (ltv['total_rev'] / ltv['users']).round(0)
dt.days / 30 — грубое приближение месяцев. Для точности используйте pd.DateOffset или relativedelta. Но на собесе и для быстрого анализа — достаточно.

Pivot: таблицы для отчётов и когорт

pivot_table — один из самых мощных инструментов pandas. Строит сводные таблицы: строки = одно измерение, столбцы = другое, ячейки = агрегат. Идеален для когортных таблиц, кросс-табуляции и отчётов.

Когортная таблица retention через pivot_tablePython
# Шаг 1: определяем когорту и период
df = events.merge(users[['user_id', 'registration_date']], on='user_id')
df['cohort'] = df['registration_date'].dt.to_period('M')
df['active_month'] = df['event_time'].dt.to_period('M')
df['period'] = (df['active_month'] - df['cohort']).apply(lambda x: x.n)

# Шаг 2: считаем уникальных юзеров по когорте × периоду
cohort_data = (df
  .groupby(['cohort', 'period'])
  .agg(users=('user_id', 'nunique'))
  .reset_index()
)

# Шаг 3: pivot — строки = когорты, столбцы = периоды
cohort_table = cohort_data.pivot_table(
  index='cohort',
  columns='period',
  values='users',
)

# Шаг 4: делим на размер когорты → retention в процентах
cohort_sizes = cohort_table[0]   # период 0 = вся когорта
retention = cohort_table.div(cohort_sizes, axis=0).round(3) * 100
Простой pivot: выручка по месяцам и платформамPython
orders['month'] = orders['order_date'].dt.to_period('M')

revenue_pivot = orders.pivot_table(
  index='month',
  columns='platform',
  values='amount',
  aggfunc='sum',
  fill_value=0,
)
4
Задача: когортный LTV — pivot_table
Постройте когортную таблицу кумулятивного LTV: строки = когорта (месяц регистрации), столбцы = период (0, 1, 2, 3...), значения = средний кумулятивный доход на пользователя.
Показать решение
РешениеPython
df = orders[orders['status']=='completed'].merge(users, on='user_id')
df['cohort'] = df['registration_date'].dt.to_period('M')
df['period'] = ((df['order_date']-df['registration_date']).dt.days // 30)

# ARPU по когорте × периоду
rev = (df.groupby(['cohort','period'])
  .agg(revenue=('amount','sum'))
  .reset_index()
)
sizes = users.groupby(users['registration_date'].dt.to_period('M')).size()
rev['arpu'] = rev.apply(
  lambda r: r['revenue'] / sizes.get(r['cohort'], 1), axis=1
)

# Pivot + cumsum по строке = кумулятивный LTV
ltv_pivot = (rev.pivot_table(
    index='cohort', columns='period', values='arpu')
  .cumsum(axis=1)
  .round(0)
)
.cumsum(axis=1) — кумулятивная сумма по столбцам (по строке). Это аналог SUM OVER(ORDER BY period) в SQL. Результат — таблица, где каждая ячейка = средний LTV когорты на горизонте N месяцев.

Создание новых колонок и цепочки

Полезные приёмыPython
# Новая колонка через assign (chainable)
df = (orders
  .assign(
    month    = lambda x: x['order_date'].dt.to_period('M'),
    is_big   = lambda x: x['amount'] > 5000,
    week_day = lambda x: x['order_date'].dt.day_name(),
  )
)

# Бины (аналог CASE WHEN в SQL)
orders['amount_bucket'] = pd.cut(
  orders['amount'],
  bins=[0, 500, 2000, 10000, float('inf')],
  labels=['0-500', '500-2K', '2K-10K', '10K+'],
)

# Процент от общего (аналог SUM OVER())
stats = orders.groupby('platform')['amount'].sum()
stats_pct = (stats / stats.sum() * 100).round(1)

# Быстрая визуализация
monthly_rev = orders.groupby(orders['order_date'].dt.to_period('M'))['amount'].sum()
monthly_rev.plot(kind='bar', title='Revenue by month')

Шпаргалка: SQL → pandas

SQLpandasПример
SELECT coldf[['col']]orders[['user_id', 'amount']]
WHERE col = 'x'df[df['col'] == 'x']orders[orders['status'] == 'completed']
WHERE col IN (...)df[df['col'].isin([...])]users[users['platform'].isin(['ios','android'])]
ORDER BY col DESCdf.sort_values('col', ascending=False)orders.sort_values('amount', ascending=False)
LIMIT 10df.head(10)orders.head(10)
GROUP BY + SUMdf.groupby('col')['val'].sum()orders.groupby('platform')['amount'].sum()
COUNT(DISTINCT col)df['col'].nunique()orders['user_id'].nunique()
LEFT JOINdf.merge(df2, on='key', how='left')orders.merge(users, on='user_id', how='left')
DATE_TRUNC('month')df['date'].dt.to_period('M')orders['order_date'].dt.to_period('M')
CASE WHENpd.cut() / np.where()pd.cut(df['amount'], bins=[0,500,5000,inf])
SUM() OVER(PARTITION BY)df.groupby('col')['val'].transform('sum')orders.groupby('user_id')['amount'].transform('sum')
Pivot (CASE WHEN)df.pivot_table(...)df.pivot_table(index='month', columns='platform')

Связанные материалы

Главное про pandas для аналитика

Вам не нужно знать весь pandas. Для 90% задач продуктового аналитика достаточно: read_csv/read_sql → фильтрация через булеву маску → groupby + agg → merge → pivot_table. Пять инструментов — и вы закрываете когорты, воронки, LTV, сегментацию.

Ключевой совет: если вы знаете SQL, думайте «как бы я написал это в SQL?» — и ищите pandas-эквивалент в шпаргалке. Со временем pandas-способ станет привычным и часто — более удобным для трансформаций.

Следующий шаг: скачайте любой e-commerce датасет (Olist на Kaggle — отлично подходит) и решите все 4 задачи из этой статьи на реальных данных. Это займёт 30 минут — и после этого вы будете уверены в pandas.

АТ
Андрей Тарасенко
// Продуктовый аналитик · Авито · Ментор

В Авито pandas — ежедневный инструмент наравне с SQL. Выгрузил данные, покрутил в pandas, построил когорты, отдал PM-у. Эта статья — тот минимум, который нужен с первого дня.

Написать в Telegram