Урок курса
groupby и agg: сводные показатели с явными именами агрегатов
NumPy и pandas: практический тренажёрДо этого урока мы работали с данными построчно или целыми столбцами. Теперь задача другая: не просто посчитать среднее по всей таблице, а получить отдельный агрегат для каждой группы строк — например, суммарные продажи по каждому региону или среднюю цену по каждой категории товара. Именно для этого нужен groupby.
groupby по одному столбцу: механика разбиения и индекс результата
Когда пишешь df.groupby('region'), pandas не вычисляет ничего сразу. Он разбивает DataFrame на группы — по одной на каждое уникальное значение столбца region — и возвращает объект DataFrameGroupBy. Это просто инструкция «как делить»; результат появится только после вызова агрегирующего метода.
Посмотрим на минимальном примере:
import pandas as pd
df = pd.DataFrame({
'region': ['Север', 'Юг', 'Север', 'Юг', 'Север'],
'sales': [100, 200, 150, 300, 80]
})
result = df.groupby('region')['sales'].sum()
print(result)
Вывод:
region Север 330 Юг 500 Name: sales, dtype: int64
Обрати внимание: region здесь не столбец, а индекс результата. Pandas переносит ключ группировки в индекс по умолчанию — это сознательное поведение, а не баг. Пока это Series, всё выглядит терпимо. Но когда результат становится DataFrame с несколькими агрегатами, индекс в роли ключа создаёт неудобства: ключ пропадает из result.columns, и дальнейшая работа с таблицей усложняется.
Чтобы ключ остался обычным столбцом, используй параметр as_index=False:
result = df.groupby('region', as_index=False)['sales'].sum()
print(result)
print(result.columns.tolist())
Вывод:
region sales
0 Север 330
1 Юг 500
['region', 'sales']
Теперь region — обычный столбец с целочисленным RangeIndex. Именно эта форма удобна для дальнейшей фильтрации, сортировки или объединения таблиц.
Концептуально groupby делает три шага: split — разбивает DataFrame на группы по значениям ключевого столбца; apply — применяет агрегирующую функцию к каждой группе; combine — собирает результаты в одну таблицу. Этот паттерн split-apply-combine лежит в основе всей работы с groupby в pandas.
В следующем разделе мы подключим к groupby вызов .agg() с именованными агрегатами, чтобы за один раз вычислять несколько показателей с читаемыми именами столбцов.
Named aggregation в agg: несколько агрегатов за один вызов
Когда у тебя есть сгруппированный объект, следующий вопрос — что именно вычислять по каждой группе. Самый гибкий способ — вызов .agg() с именованными парами.
Синтаксис выглядит так:
df.groupby('region', as_index=False).agg(
total_sales=('sales', 'sum'),
avg_units=('units', 'mean')
)
Каждая именованная пара — это отдельный аргумент вида имя_результата=(столбец, функция). Слева от = — имя, которое получит новый столбец в итоговой таблице. В кортеже справа — исходный столбец, из которого берутся данные, и строковое имя агрегирующей функции.
В этом курсе используются строки 'sum', 'mean', 'min', 'max', 'count' — это не исчерпывающий список агрегатов pandas, но достаточный для большинства задач. Их можно комбинировать в любом порядке и количестве в одном вызове.
Важный нюанс про 'count': он считает непропущенные значения в указанном столбце. Если в sales есть NaN, такая строка не попадёт в счётчик. Это поведение соответствует логике «сколько реальных продаж зафиксировано», а не «сколько строк в группе».
Вот полный пример с несколькими агрегатами одновременно:
import pandas as pd
df = pd.DataFrame({
'region': ['Север', 'Юг', 'Север', 'Юг', 'Север'],
'category': ['A', 'A', 'B', 'B', 'A'],
'sales': [100, 200, 150, 300, 80],
'units': [10, 20, 15, 30, 8]
})
result = df.groupby('region', as_index=False).agg(
total_sales=('sales', 'sum'),
avg_units=('units', 'mean'),
min_sales=('sales', 'min'),
nonmissing_count=('sales', 'count')
)
print(result)
Вывод:
region total_sales avg_units min_sales nonmissing_count 0 Север 330 11.0 80 3 1 Юг 500 25.0 200 2
Четыре именованные пары — четыре столбца в результате. Каждый столбец берёт данные из нужного исходного столбца независимо: total_sales и min_sales смотрят в sales, avg_units — в units, nonmissing_count считает непропущенные значения в sales.
Главное преимущество такого синтаксиса — имена столбцов результата ты задаёшь явно, прямо в вызове. Без named aggregation pandas генерировал бы технические имена вроде sales или строил MultiIndex в заголовках — ни то ни другое не читается в реальном коде. С именованными парами таблица сразу выходит с понятными заголовками, которые не нужно переименовывать потом через rename().
Один вызов .agg() с несколькими именованными парами заменяет несколько последовательных вычислений и возвращает всё в одной таблице. Именно этот паттерн используется в курсе как стандартный способ агрегирования.
Два способа получить плоскую таблицу: as_index=False и reset_index()
Мы уже видели, что groupby по умолчанию превращает ключ группировки в индекс результата. Это становится проблемой, как только хочешь работать с результатом как с обычной таблицей: фильтровать по ключу, сортировать или передавать дальше по пайплайну. Посмотрим, как это проявляется на практике и как это исправить.
Ловушка: ключ пропал из columns
Если вызываешь groupby без as_index=False, ключ группировки уходит в индекс и исчезает из result.columns:
import pandas as pd
df = pd.DataFrame({
'region': ['Север', 'Юг', 'Север', 'Юг', 'Север'],
'category': ['A', 'A', 'B', 'B', 'A'],
'sales': [100, 200, 150, 300, 80],
'units': [10, 20, 15, 30, 8]
})
result = df.groupby('region').agg(
total_sales=('sales', 'sum'),
avg_units=('units', 'mean')
)
print(result.columns.tolist())
# ['total_sales', 'avg_units']
print(result.index.name)
# region
region есть — но только в индексе. В columns его нет. Если попробуешь написать result['region'], получишь KeyError. Именно здесь ломаются скрипты, где после агрегации ожидается обычная плоская таблица.
Способ 1: as_index=False прямо в groupby
Простейшее решение — передать параметр сразу:
result1 = df.groupby('region', as_index=False).agg(
total_sales=('sales', 'sum'),
avg_units=('units', 'mean')
)
print(result1)
# region total_sales avg_units
# 0 Север 330 11.0
# 1 Юг 500 25.0
print(result1.columns.tolist())
# ['region', 'total_sales', 'avg_units']
region возвращается в обычные столбцы, индекс — стандартный RangeIndex. Таблица сразу готова к любым операциям.
Способ 2: reset_index() после agg
Если по каким-то причинам написал groupby без as_index=False, можно добавить reset_index() в конце цепочки:
result2 = df.groupby('region').agg(
total_sales=('sales', 'sum'),
avg_units=('units', 'mean')
).reset_index()
print(result2)
# region total_sales avg_units
# 0 Север 330 11.0
# 1 Юг 500 25.0
Результат идентичен. reset_index() вынимает ключ из индекса и помещает его первым столбцом — именно то, что делает as_index=False автоматически.
Какой способ выбирать
Оба способа корректны и дают одинаковую таблицу. На практике as_index=False лаконичнее: параметр виден сразу при чтении строки с groupby, и не нужно помнить о дополнительном вызове в конце. reset_index() полезен, когда получаешь уже сгруппированный результат и хочешь его «распрямить» постфактум.
В этом курсе для группировки по одному ключу оба варианта используются; для нескольких ключей — только as_index=False, чтобы не сталкиваться с составными типами индекса.
groupby по нескольким столбцам с as_index=False
Один ключ группировки — это частный случай. Чаще в реальных данных нужно смотреть на комбинации: например, продажи по каждому региону внутри каждой категории. Для этого в groupby передаётся список столбцов.
Синтаксис не меняется — добавляется только список вместо одной строки:
import pandas as pd
df = pd.DataFrame({
'region': ['Север', 'Юг', 'Север', 'Юг', 'Север'],
'category': ['A', 'A', 'B', 'B', 'A'],
'sales': [100, 200, 150, 300, 80],
'units': [10, 20, 15, 30, 8]
})
result = df.groupby(['region', 'category'], as_index=False).agg(
total_sales=('sales', 'sum'),
avg_units=('units', 'mean')
)
print(result)
Вывод:
region category total_sales avg_units 0 Север A 180 9.0 1 Север B 150 15.0 2 Юг A 200 20.0 3 Юг B 300 30.0
Оба ключа — region и category — остаются обычными столбцами. Индекс — стандартный RangeIndex. Таблица плоская и сразу готова к фильтрации или сортировке.
Что здесь происходит: pandas находит все уникальные комбинации значений region и category, считает агрегаты по строкам, попавшим в каждую комбинацию, и собирает результат. Строки ('Север', 'A') из оригинального DataFrame — это индексы 0 и 4, их sales суммируется как 100 + 80 = 180.
Параметр as_index=False здесь особенно важен. Без него pandas создал бы составной индекс из двух уровней — один для region, другой для category. Работать с таким результатом через стандартные операции заметно сложнее. Поэтому для многоключевой группировки as_index=False — единственный рекомендуемый паттерн в этом курсе.
Если нужно добавить третий ключ — просто расширяешь список: groupby(['region', 'category', 'month'], as_index=False). Количество ключей не ограничено; синтаксис agg с именованными парами при этом не меняется.
В следующем уроке groupby появится в другой роли: вместо agg, который сжимает таблицу до одной строки на группу, будет transform — он возвращает групповой показатель для каждой строки исходного DataFrame, не меняя его длину.
Закрепите тему на задачах
Определите гранулярность результата: одна строка на группу. Сверьте сумму групп с исходной таблицей и учтите поведение группировки с пустым ключом.
groupby и agg: сводные показатели с явными именами агрегатов — задача по NumPy и pandas
pandas groupby и agg: сводка по двум ключам
pandas groupby и agg: группировка, rename и sort_values
Откройте условие и решите задачу перед чтением следующей темы. В задачах на код проверяется программа, в тестовых заданиях — выбранный ответ. Для последовательного обучения вернитесь в программу бесплатного курса.
Задача 1
DataFrame df содержит столбцы 'city', 'amount', 'count'. Выполнен код:
result = df.groupby('city').agg(
total=('amount', 'sum'),
n=('count', 'sum')
)
Чем является result.index?
Задача 2
Дан код:
import pandas as pd
df = pd.DataFrame({
'team': ['Alpha', 'Beta', 'Alpha', 'Beta'],
'score': [80, 90, 70, 60]
})
result = df.groupby('team', as_index=False).agg(
max_score=('score', 'max'),
min_score=('score', 'min')
)
Чему равен result.loc[result['team'] == 'Alpha', 'max_score'].iloc[0]?
Задача 3
Один ключ, два способа плоской таблицы: as_index=False и reset_index
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.
Что нужно сделать
Напишите программу, которая:
- Читает из
stdinцелое числоn— количество строк таблицы. - Читает следующие
nстрок; каждая содержит три токена через пробел:store(строка без пробелов),qty(целое число),revenue(целое число). Пример строки:Alpha 10 5000. - Создаёт
pd.DataFrameиз словаря списков с ключами'store','qty','revenue'. - Способ A. Группирует DataFrame по
'store'сas_index=Falseи вычисляет за один вызовaggдва агрегата:total_qty=('qty', 'sum')иtotal_rev=('revenue', 'sum'). Сохраняет результат вresult_a. - Способ B. Группирует тот же DataFrame по
'store'безas_index=Falseи вычисляет те же два агрегата с теми же именами. Затем применяет.reset_index(). Сохраняет вresult_b. - Выводит в
stdoutтри служебные строки, а затем по одной строке для каждой строкиresult_a:
columns_a=<список>—list(result_a.columns)в виде Python-списка;columns_b=<список>—list(result_b.columns);equal=<bool>— результатresult_a.equals(result_b)(True если таблицы идентичны);- для каждой строки
result_a, отсортированной по'store', выводит одну строку вида<store> <total_qty> <total_rev>.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: n >= 1.
Формат ввода
n store1 qty1 rev1 store2 qty2 rev2 ...
Формат вывода
columns_a=['store', 'total_qty', 'total_rev']
columns_b=['store', 'total_qty', 'total_rev']
equal=True
<store> <total_qty> <total_rev>
...
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 4
Два ключа, четыре агрегата: groupby по region и channel с as_index=False
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.
Что нужно сделать
Напишите программу, которая:
- Читает из
stdinцелое числоn— количество строк таблицы. - Читает следующие
nстрок; каждая содержит четыре токена через пробел:region(строка без пробелов),channel(строка без пробелов),amount(целое число),orders(целое число). Пример строки:Север online 5000 10. - Создаёт
pd.DataFrameиз словаря списков с ключами'region','channel','amount','orders'. - Группирует DataFrame по
['region', 'channel']сas_index=Falseи вычисляет за один вызовaggчетыре именованных агрегата:
total_amount=('amount', 'sum')avg_amount=('amount', 'mean')max_orders=('orders', 'max')tx_count=('orders', 'count')
Сохраняет результат в переменную result.
5. Выводит в stdout:
- строку
columns=<список>—list(result.columns)в виде Python-списка; - строку
shape=<кортеж>—result.shape; - для каждой строки
result, отсортированной по'region', затем по'channel', выводит одну строку вида<region> <channel> <total_amount> <avg_amount> <max_orders> <tx_count>, гдеavg_amountокруглён до двух знаков после запятой.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: n >= 1.
Формат ввода
n region1 channel1 amount1 orders1 region2 channel2 amount2 orders2 ...
Формат вывода
columns=['region', 'channel', 'total_amount', 'avg_amount', 'max_orders', 'tx_count']
shape=(<строк>, 6)
<region> <channel> <total_amount> <avg_amount> <max_orders> <tx_count>
...
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 5
Найди и исправь: три ошибки в groupby, agg и reset_index
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет.
В редакторе уже есть сломанный код. В нём ровно три ошибки в трёх независимых строках:
- В первой группировке используется
groupby('dept', as_index=True), хотя нужно получить плоскую таблицу с'dept'как обычным столбцом — нуженas_index=False. - В именованном агрегате
avg_bonus=('bonus', 'mean')исходный столбец написан как'salary'вместо'bonus'— среднее считается не по тому столбцу. - Вторая группировка вычисляет
total_bonus=('bonus', 'sum'), но после.agg(...)не вызван.reset_index(), из-за чего'dept'и'name'остаются индексом, а не столбцами.
Каждая ошибка находится в отдельной строке кода. Исправьте все три.
Что нужно сделать
Исправьте три ошибки так, чтобы программа:
- Читала из
stdinцелое числоn— количество строк таблицы. - Читала следующие
nстрок; каждая содержит четыре токена через пробел:dept(строка),name(строка),salary(целое число),bonus(целое число). - Строила
pd.DataFrameс именемdfи столбцами'dept','name','salary','bonus'. - Выполняла первую группировку по
'dept'сas_index=Falseи вычисляла за одинaggтри агрегата:
total_sal=('salary', 'sum')avg_bonus=('bonus', 'mean')← исходный столбец должен быть'bonus', не'salary'head_count=('name', 'count')
Сохраняла в summary. Выводила list(summary.columns), затем каждую строку summary, отсортированную по 'dept', в формате <dept> <total_sal> <avg_bonus> <head_count>.
- Выполняла вторую группировку по
['dept', 'name']безas_index=Falseи вычислялаtotal_bonus=('bonus', 'sum'), затем вызывала.reset_index(). Сохраняла вby_person. Выводилаlist(by_person.columns), затем каждую строкуby_person, отсортированную по'dept', потом по'name', в формате<dept> <name> <total_bonus>.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: n >= 1.
Формат ввода
n dept1 name1 salary1 bonus1 dept2 name2 salary2 bonus2 ...
Формат вывода
['dept', 'total_sal', 'avg_bonus', 'head_count']
<dept> <total_sal> <avg_bonus> <head_count>
...
['dept', 'name', 'total_bonus']
<dept> <name> <total_bonus>
...
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 6
Пайплайн: groupby + agg + rename + sort, затем группировка по двум ключам
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.
Что нужно сделать
Напишите программу, которая:
- Читает из
stdinцелое числоn— количество строк таблицы. - Читает следующие
nстрок; каждая содержит четыре токена через пробел:month(строка без пробелов),category(строка без пробелов),amount(целое число),transactions(целое число). Пример строки:Jan Food 1200 12. - Создаёт
pd.DataFrameиз словаря списков с ключами'month','category','amount','transactions'. - Шаг A. Группирует DataFrame по
'month'сas_index=Falseи вычисляет за один вызовaggтри именованных агрегата:month_total=('amount', 'sum'),month_avg=('amount', 'mean'),month_tx=('transactions', 'sum'). Сохраняет вmonthly. - Шаг B. Переименовывает столбец
'month_total'в'total'черезrename(columns=...)и сохраняет результат обратно вmonthly. - Шаг C. Сортирует
monthlyпо'total'по возрастанию черезsort_values. Выводит строкуmonthly_columns=<список>—list(monthly.columns), затем для каждой строки отсортированногоmonthlyвыводит одну строку вида<month> <total> <round(month_avg, 1)> <month_tx>. - Шаг D. Группирует исходный DataFrame по
['month', 'category']сas_index=Falseи вычисляетcat_total=('amount', 'sum'). Сохраняет вby_cat. Сортируетby_catпо'month', затем по'category'. Выводит строкуby_cat_columns=<список>, затем для каждой строки выводит<month> <category> <cat_total>.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: n >= 1.
Формат ввода
n month1 category1 amount1 transactions1 month2 category2 amount2 transactions2 ...
Формат вывода
monthly_columns=['month', 'total', 'month_avg', 'month_tx']
<month> <total> <month_avg> <month_tx>
...
by_cat_columns=['month', 'category', 'cat_total']
<month> <category> <cat_total>
...
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задачи к уроку
- groupby и agg: сводные показатели с явными именами агрегатов — задача по NumPy и pandas
Практическая задача по теме «groupby и agg: сводные показатели с явными именами агрегатов» с проверкой ответа онлайн.
- pandas groupby и agg: группировка по двум столбцам
Группировка pandas по нескольким столбцам: именованные агрегаты, сумма, среднее и ключи в обычных столбцах. Пример и задача с проверкой кода.
- pandas groupby и agg: именованные агрегаты и сортировка
Как посчитать сумму и среднее по группам pandas, задать имена столбцов и отсортировать итог? Пример и задача с проверкой.
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
