Задачи на агрегацию в pandas

Карьерник — квиз-тренажёр в Telegram с 1500+ задач и вопросов для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем эти задачи

Группировка — ежедневная работа аналитика в pandas: сумма по категориям, среднее по когортам, процентили по сегментам, running totals для отчётов. groupby + agg / transform / apply — одна из тем, которую 100% спросят на собесе по Python.

На собеседованиях любят проверять нюансы: разница agg и transform (первая возвращает N_groups строк, вторая — len(df)), когда медленно работает apply vs встроенные функции, как применить разные агрегации к разным колонкам, как отфильтровать группы по условию (groupby().filter).

Ниже — 12 задач с решениями на типовом датасете (user_id, category, amount, date). От простых groupby().sum() до продвинутых паттернов (топ-N в группе, running total, доля в категории через transform, conditional aggregation).

Dataset

import pandas as pd

df = pd.DataFrame({
    'user_id':  [1, 1, 2, 2, 3, 3, 3],
    'category': ['A', 'B', 'A', 'A', 'B', 'B', 'A'],
    'amount':   [100, 200, 150, 250, 300, 100, 200],
    'date':     pd.to_datetime(['2026-01-01', '2026-01-02', '2026-01-01',
                                '2026-02-01', '2026-02-01', '2026-03-01', '2026-03-02'])
})

Задача 1. Сумма по пользователю

df.groupby('user_id')['amount'].sum()

Задача 2. Количество заказов на пользователя

df.groupby('user_id').size()
# или
df.groupby('user_id')['amount'].count()

Задача 3. Несколько агрегатов одновременно

df.groupby('user_id').agg(
    total=('amount', 'sum'),
    avg=('amount', 'mean'),
    count=('amount', 'count')
)

Задача 4. Средний amount по (user_id, category)

df.groupby(['user_id', 'category'])['amount'].mean()

Задача 5. Pivot по месяцу × категории

df['month'] = df['date'].dt.to_period('M')
df.pivot_table(
    values='amount',
    index='month',
    columns='category',
    aggfunc='sum',
    fill_value=0
)

Задача 6. Пользователи с 2+ заказами

counts = df.groupby('user_id').size()
df[df['user_id'].isin(counts[counts >= 2].index)]

# или через filter
df.groupby('user_id').filter(lambda g: len(g) >= 2)

Задача 7. Топ-2 заказа в каждой категории

df.sort_values(['category', 'amount'], ascending=[True, False]) \
  .groupby('category') \
  .head(2)

Задача 8. Running total в группе

df['running_total'] = df.sort_values('date') \
    .groupby('user_id')['amount'].cumsum()

Задача 9. Доля пользователя в категории

df['share_in_category'] = df['amount'] / \
    df.groupby('category')['amount'].transform('sum')

Задача 10. Первое и последнее значение в группе

df.groupby('user_id').agg(
    first_date=('date', 'min'),
    last_date=('date', 'max'),
    first_amount=('amount', 'first'),
    last_amount=('amount', 'last')
)

Но first/last работает только на отсортированных данных. Используйте head(1) / tail(1):

df.sort_values('date').groupby('user_id').first()
df.sort_values('date').groupby('user_id').last()

Задача 11. Custom aggregation

def range_func(s):
    return s.max() - s.min()

df.groupby('user_id')['amount'].agg(range_func)

Задача 12. Conditional aggregation

# сколько у каждого user заказов дороже 100
df.groupby('user_id').apply(lambda g: (g['amount'] > 100).sum())

# или
df.assign(over_100=df['amount'] > 100).groupby('user_id')['over_100'].sum()

Частые ошибки

  • Забыть колонку после groupby → агрегирует все числовые
  • transform vs agg — разные результаты по размеру
  • Slow apply — для простых операций быстрее встроенные
  • Забыть reset_index — MultiIndex потом путает

Связанные темы

FAQ

groupby или pivot_table?

Для одной агрегации — groupby. Для wide-формата — pivot_table.

transform vs agg?

Transform возвращает len(df). Agg — len(groups).


Тренируйте pandas — откройте тренажёр с 1500+ задач для собесов.