Группировка: groupby() и агрегация (agg)

Группировка: groupby() и агрегация (agg) Pandas

Привет! Ты уже умеешь фильтровать данные, добавлять колонки и заменять значения. Но часто нужно не просто смотреть на строки по отдельности, а собирать их в группы и вычислять общие статистики. Например, «какая средняя зарплата в каждом городе» или «сколько продаж было по месяцам».

В этой статье мы разберём:

  • Что такое groupby() и зачем он нужен
  • Группировка по одной и нескольким колонкам
  • Агрегация данных с agg() и встроенными функциями
  • Несколько агрегаций одновременно
  • Создание своих агрегаций

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный Pandas (pip install pandas)
  • Базовое понимание DataFrame и колонок
  • Желание агрегировать данные

Совет: groupby() — это аналог GROUP BY в SQL. Если ты работал с базами данных, ты уже понимаешь логику. Если нет — это очень полезный инструмент для анализа данных.

Основная часть

Подготовка данных

import pandas as pd
import numpy as np

np.random.seed(42)

# Создаём данные о продажах
data = {
    'Товар': ['Ноутбук', 'Телефон', 'Планшет', 'Ноутбук', 'Телефон', 'Планшет'] * 3,
    'Продажи': np.random.randint(1, 20, 18),
    'Цена': [50000, 30000, 15000] * 6,
    'Город': ['Москва'] * 6 + ['СПб'] * 6 + ['Казань'] * 6,
    'Магазин': ['A'] * 9 + ['B'] * 9
}

df = pd.DataFrame(data)
df['Выручка'] = df['Продажи'] * df['Цена']

print("Исходные данные:")
print(df.head(10))

Результат:

    Товар  Продажи   Цена  Город Магазин   Выручка
0  Ноутбук        7  50000  Москва       A   350000
1  Телефон       10  30000  Москва       A   300000
2  Планшет       11  15000  Москва       A   165000
3  Ноутбук       14  50000  Москва       A   700000
4  Телефон        6  30000  Москва       A   180000
5  Планшет       10  15000  Москва       A   150000
6  Ноутбук        8  50000    СПб       A   400000
7  Телефон        4  30000    СПб       A   120000
8  Планшет        9  15000    СПб       A   135000
9  Ноутбук        4  50000    СПб       B   200000

Совет: Мы создали данные с тремя городами, двумя магазинами и тремя товарами. Это идеальный набор для изучения группировки.

Основы groupby()

groupby() группирует строки по значениям в указанной колонке. После группировки к каждой группе можно применить агрегирующую функцию.

Группировка по одной колонке

# Группируем по товару и считаем сумму продаж
grouped = df.groupby('Товар')['Продажи'].sum()
print(grouped)

Результат:

Товар
Ноутбук     45
Планшет     55
Телефон     47
Name: Продажи, dtype: int64

Группировка с несколькими агрегациями

# Группируем по товару и считаем сумму, среднее и количество
grouped = df.groupby('Товар')['Продажи'].agg(['sum', 'mean', 'count'])
print(grouped)

Результат:

         sum      mean  count
Товар                        
Ноутбук   45  7.500000      6
Планшет   55  9.166667      6
Телефон   47  7.833333      6

Совет: После группировки у тебя появляется объект DataFrameGroupBy. Ты можешь применять к нему разные агрегирующие функции.

Агрегация с agg()

agg() — самый гибкий способ агрегации. Он позволяет указать несколько функций сразу.

Разные агрегации для разных колонок

# Для колонки 'Продажи' считаем сумму и среднее, для 'Цена' — максимум
result = df.groupby('Товар').agg({
    'Продажи': ['sum', 'mean'],
    'Цена': 'max'
})
print(result)

Результат:

         Продажи          Цена
             sum      mean   max
Товар                          
Ноутбук       45  7.500000 50000
Планшет       55  9.166667 15000
Телефон       47  7.833333 30000

Пользовательские агрегации через agg()

# Создаём свою функцию
def price_range(x):
    return x.max() - x.min()

result = df.groupby('Город')['Цена'].agg(price_range)
print(result)

Совет: В agg() можно передать и лямбда-функции. Это удобно для быстрых расчётов.

Группировка по нескольким колонкам

# Группируем по Городу и Товару
grouped = df.groupby(['Город', 'Товар'])['Продажи'].sum()
print(grouped)

Результат:

Город    Товар   
Казань   Ноутбук     10
         Планшет     15
         Телефон      9
СПб      Ноутбук     16
         Планшет     19
         Телефон     12
Москва   Ноутбук     19
         Планшет     21
         Телефон     26
Name: Продажи, dtype: int64

Преобразование в DataFrame

# .reset_index() превращает результат обратно в DataFrame
grouped_df = df.groupby(['Город', 'Товар'])['Продажи'].sum().reset_index()
print(grouped_df)

Результат:

     Город   Товар  Продажи
0   Казань  Ноутбук       10
1   Казань  Планшет       15
2   Казань  Телефон        9
3      СПб  Ноутбук       16
4      СПб  Планшет       19
5      СПб  Телефон       12
6   Москва  Ноутбук       19
7   Москва  Планшет       21
8   Москва  Телефон       26

Совет: reset_index() превращает мультииндекс в обычные колонки. Это часто удобно для дальнейшей работы.

Агрегация с pivot_table()

pivot_table() — это более мощная альтернатива groupby(), которая строит сводные таблицы.

# Строим сводную таблицу: Город vs Товар, значения — продажи
pivot = df.pivot_table(
    values='Продажи',
    index='Город',
    columns='Товар',
    aggfunc='sum'
)
print(pivot)

Результат:

Товар   Ноутбук  Планшет  Телефон
Город                            
Казань       10       15        9
СПб          16       19       12
Москва       19       21       26

Применение apply() после группировки

apply() позволяет применять более сложные функции к каждой группе.

# Для каждой группы (города) вычисляем среднее и создаём колонку с процентом
def add_percent(group):
    group['Процент'] = (group['Продажи'] / group['Продажи'].sum()) * 100
    return group

result = df.groupby('Город').apply(add_percent)
print(result[['Город', 'Товар', 'Продажи', 'Процент']])

Результат:

     Город   Товар  Продажи    Процент
0   Москва  Ноутбук        7  12.500000
1   Москва  Телефон       10  17.857143
2   Москва  Планшет       11  19.642857
3   Москва  Ноутбук       14  25.000000
4   Москва  Телефон        6  10.714286
5   Москва  Планшет       10  17.857143
...

Совет: apply() мощнее agg(), но может быть медленнее для больших данных. Используй его, когда нужна сложная логика.

transform() — добавление агрегатов в исходную таблицу

transform() возвращает результат той же формы, что и исходные данные. Это удобно для добавления агрегированных значений в таблицу.

# Добавляем средние продажи для каждого города в исходную таблицу
df['Средние_продажи_по_городу'] = df.groupby('Город')['Продажи'].transform('mean')
print(df[['Город', 'Товар', 'Продажи', 'Средние_продажи_по_городу']])

Результат:

     Город   Товар  Продажи  Средние_продажи_по_городу
0   Москва  Ноутбук        7                   11.333333
1   Москва  Телефон       10                   11.333333
2   Москва  Планшет       11                   11.333333
3   Москва  Ноутбук       14                   11.333333
4   Москва  Телефон        6                   11.333333
5   Москва  Планшет       10                   11.333333
6      СПб  Ноутбук        8                   12.333333
7      СПб  Телефон        4                   12.333333
8      СПб  Планшет        9                   12.333333
9      СПб  Ноутбук        4                   12.333333
...

Практический пример: анализ продаж по категориям

import pandas as pd
import numpy as np

np.random.seed(42)

# Данные о продажах
data = {
    'Товар': ['Ноутбук', 'Телефон', 'Планшет'] * 20,
    'Продажи': np.random.randint(1, 20, 60),
    'Цена': [50000, 30000, 15000] * 20,
    'Город': np.random.choice(['Москва', 'СПб', 'Казань'], 60),
    'Магазин': np.random.choice(['A', 'B'], 60)
}

df = pd.DataFrame(data)
df['Выручка'] = df['Продажи'] * df['Цена']

print("=== 1. Общая статистика по товарам ===")
print(df.groupby('Товар')['Выручка'].agg(['sum', 'mean', 'count']))

print("\n=== 2. Продажи по городам ===")
print(df.groupby('Город')['Выручка'].sum())

print("\n=== 3. Лучший товар в каждом городе ===")
best = df.groupby(['Город', 'Товар'])['Выручка'].sum().reset_index()
best = best.loc[best.groupby('Город')['Выручка'].idxmax()]
print(best)

print("\n=== 4. Сводная: Город vs Товар ===")
pivot = df.pivot_table(
    values='Выручка',
    index='Город',
    columns='Товар',
    aggfunc='sum'
)
print(pivot)

print("\n=== 5. Добавляем средние выручки в данные ===")
df['Средняя_выручка_по_товару'] = df.groupby('Товар')['Выручка'].transform('mean')
print(df[['Товар', 'Выручка', 'Средняя_выручка_по_товару']].head())

Задачи для закрепления

Задача 1. Сгруппируй данные по колонке Город и посчитай среднюю зарплату.

Задача 2. Сгруппируй по двум колонкам (ГородСтатус) и посчитай сумму зарплат.

Задача 3. С помощью agg() посчитай минимум, максимум и среднее для зарплат по городам.

Задача 4. Добавь в исходные данные колонку со средним возрастом для каждой группы (используй transform).

Задача 5. Построй сводную таблицу с помощью pivot_table().

Ответы:

Задача 1.

df.groupby('Город')['Зарплата'].mean()

Задача 2.

df.groupby(['Город', 'Статус'])['Зарплата'].sum()

Задача 3.

df.groupby('Город')['Зарплата'].agg(['min', 'max', 'mean'])

Задача 4.

df['Средний_возраст_по_группе'] = df.groupby('Город')['Возраст'].transform('mean')

Задача 5.

pivot = df.pivot_table(values='Зарплата', index='Город', columns='Статус', aggfunc='mean')

Нюансы и подводные камни

Группировка с пропусками

Если в колонке есть NaN, строки с ними не попадают в группы. Чтобы включить их, используй dropna=False.

df.groupby('Колонка', dropna=False)['Значение'].mean()

Производительность

Для больших данных groupby() может быть медленным. Используй agg() с векторизованными функциями (например, встроенными) вместо apply().

Сортировка результата

По умолчанию groupby() сортирует группы по ключам. Отключить можно с помощью sort=False.

df.groupby('Колонка', sort=False)['Значение'].sum()

Частые ошибки и как их избежать

Ошибка 1: Забыл указать колонку для агрегации

# Ошибка
df.groupby('Город').sum()

# Правильно — указываем колонку
df.groupby('Город')['Зарплата'].sum()

Ошибка 2: Неправильный синтаксис agg()

# Ошибка
df.groupby('Город').agg('sum', 'mean')

# Правильно
df.groupby('Город')['Зарплата'].agg(['sum', 'mean'])

Ошибка 3: Использование apply() вместо agg()

# Медленно для простых операций
df.groupby('Город')['Зарплата'].apply(lambda x: x.sum())

# Быстро для встроенных функций
df.groupby('Город')['Зарплата'].sum()

Шпаргалка

Что нужноКак пишется
Группировка по одной колонкеdf.groupby('Колонка')['Значение'].sum()
Группировка по нескольким колонкамdf.groupby(['Кол1', 'Кол2'])['Значение'].sum()
Несколько агрегацийdf.groupby('Колонка')['Значение'].agg(['sum', 'mean'])
Разные агрегации для разных колонокdf.groupby('Колонка').agg({'Кол1': 'sum', 'Кол2': 'mean'})
Пользовательская агрегацияdf.groupby('Колонка')['Значение'].agg(функция)
Добавление агрегата в данныеdf.groupby('Колонка')['Значение'].transform('mean')
Сложная логикаdf.groupby('Колонка').apply(функция)
Сводная таблицаdf.pivot_table(values='x', index='Кол1', columns='Кол2', aggfunc='sum')

Заключение

Сегодня мы научились:

  • Группировать данные по одной и нескольким колонкам
  • Применять агрегирующие функции с agg()
  • Использовать встроенные и пользовательские агрегации
  • Строить сводные таблицы с pivot_table()
  • Добавлять агрегированные значения в данные с transform()

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×