Нашли ошибку?
среднийpandas 3.0.6

Группировка: groupby

Посчитать итоги по категориям — сумму продаж по городам, среднюю оценку по товарам.

Нужно знать:Выбор столбцовОписательная статистика

Содержание статьи

Коротко

  • groupby работает по схеме разделение → применение → объединение (split-apply-combine): строки делятся на группы по ключу, к каждой группе применяется функция, результаты собираются в одну таблицу.
  • Сам df.groupby("city") ничего не считает — это «ленивый» объект; результат появляется после агрегации: .sum(), .mean(), .agg(...).
  • Ключи группировки по умолчанию уходят в индекс результата и сортируются.
  • size() — число строк в группе, count() — число непустых значений в столбце.
  • Строки с NaN в ключе по умолчанию выпадают (dropna=True).

Синтаксис

df.groupby(by, as_index=True, sort=True, dropna=True, observed=True)
df.groupby(by)["столбец"].sum()          # → Series
df.groupby(by)[["a", "b"]].mean()        # → DataFrame
df.groupby(by)["столбец"].agg(["sum", "mean"])

Параметры

ПараметрЧто делаетПо умолчанию
byключ: имя столбца, список имён, Series или функция от метки индексаобязательный
as_indexTrue — ключи в индекс результата; False — в обычные столбцыTrue
sortсортировать группы по ключу; False — порядок первого появленияTrue
dropnaвыбрасывать строки, у которых ключ — NaNTrue
observedдля категориальных ключей: только встречающиеся категорииTrue

Примеры

Данные для примеровиспользуются во всех примерах ниже
# продажи сети кофеен за день; у одной записи нет оценки
sales = pd.DataFrame({
    "city":   ["Омск", "Тула", "Омск", "Пермь", "Тула", "Омск"],
    "drink":  ["латте", "капучино", "капучино", "латте", "латте", "латте"],
    "cups":   [30, 12, 18, 25, 20, 12],
    "rating": [4.5, 4.0, None, 4.8, 3.9, 4.2],
})
Сумма по одному ключу
sales.groupby("city")["cups"].sum()
Вывод
city
Омск     60
Пермь    25
Тула     32
Name: cups, dtype: int64
Несколько столбцов сразу
sales.groupby("city")[["cups", "rating"]].mean()
Вывод
       cups  rating
city
Омск   20.0    4.35
Пермь  25.0    4.80
Тула   16.0    3.95

mean пропускает NaN: у Омска средняя оценка посчитана по двум записям из трёх.

Несколько функций: agg
sales.groupby("city")["cups"].agg(["sum", "mean", "max"])
Вывод
       sum  mean  max
city
Омск    60  20.0   30
Пермь   25  25.0   25
Тула    32  16.0   20

Подробнее — в статье agg и именованная агрегация.

Два ключа — MultiIndex в результате
sales.groupby(["city", "drink"])["cups"].sum()
Вывод
city   drink
Омск   капучино    18
       латте       42
Пермь  латте       25
Тула   капучино    12
       латте       20
Name: cups, dtype: int64
Ключи обычными столбцами: as_index=False
sales.groupby(["city", "drink"], as_index=False)["cups"].sum()
Вывод
    city     drink  cups
0   Омск  капучино    18
1   Омск     латте    42
2  Пермь     латте    25
3   Тула  капучино    12
4   Тула     латте    20

То же самое даёт .reset_index() после агрегации.

size и count
groups = sales.groupby("city")
pd.DataFrame({
    "size": groups.size(),               # все строки группы
    "count": groups["rating"].count(),   # только непустые значения
})
Вывод
       size  count
city
Омск      3      2
Пермь     1      1
Тула      2      2
Без сортировки групп
sales.groupby("city", sort=False)["cups"].sum()   # в порядке первого появления
Вывод
city
Омск     60
Тула     32
Пермь    25
Name: cups, dtype: int64
Посмотреть на группы: цикл по groupby
for city, part in sales.groupby("city"):
    print(city, "→ строк:", len(part), "| чашек:", part["cups"].sum())
Вывод
Омск → строк: 3 | чашек: 60
Пермь → строк: 1 | чашек: 25
Тула → строк: 2 | чашек: 32

Цикл удобен, чтобы понять, как разбились данные. Для вычислений используйте агрегации — они намного быстрее.

Подводные камни

mean() по всему DataFrame падает на текстовых столбцах

Агрегации не отбрасывают молча нечисловые столбцы. Выберите нужные столбцы или передайте numeric_only=True. При этом sum() на строках не падает, а склеивает их — тоже вряд ли то, что нужно.

Агрегация по всем столбцамошибка: TypeError
sales.groupby("city").mean()   # столбец drink — строки, среднего у них нет
Ошибка
TypeError: dtype 'str' does not support operation 'mean'

Строки с пустым ключом исчезают

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

NaN в ключе группировки
deals = pd.DataFrame({
    "manager": ["Аня", None, "Аня", "Борис"],
    "amount":  [300, 500, 200, 400],
})
print(deals.groupby("manager")["amount"].sum())
deals.groupby("manager", dropna=False)["amount"].sum()
Вывод
manager
Аня      500
Борис    400
Name: amount, dtype: int64
manager
Аня      500
Борис    400
NaN      500
Name: amount, dtype: int64

Пустые категории не попадают в результат

Категориальный ключ
visits = pd.DataFrame({
    "day": pd.Categorical(["пн", "ср", "пн"], categories=["пн", "вт", "ср"]),
    "guests": [40, 55, 35],
})
print(visits.groupby("day")["guests"].sum())
visits.groupby("day", observed=False)["guests"].sum()   # все категории, даже пустые
Вывод
day
пн    75
ср    55
Name: guests, dtype: int64
day
пн    75
вт     0
ср    55
Name: guests, dtype: int64

MultiIndex там, где его не ждали

Группировка по нескольким ключам даёт MultiIndex. Если дальше нужны обычные столбцы — например, для merge или сохранения в CSV, — передайте as_index=False или вызовите .reset_index().