2025, Oct 16 14:17
Как добавить промежуточные итоги в pandas pivot_table с MultiIndex без pd.append
Показываем, как добавить промежуточные итоги в pandas pivot_table с MultiIndex: groupby по уровню индекса, переименование, concat и сортировка без pd.append.
Добавление промежуточных итогов в pandas pivot_table — частая задача в отчетах, но многие старые примеры опираются на устаревший pd.append. Ниже — лаконичный, современный способ, который подходит, когда в сводной таблице по строкам используется MultiIndex, например index=["A", "B"], и нужны итоги по группам прямо в той же таблице.
Постановка задачи
Начнем с простого набора данных и построим сводную таблицу с иерархическими строками. Задача — добавить строки с промежуточными итогами по одному из уровней индекса, не нарушая структуру.
import pandas as pd
raw_df = pd.DataFrame({
    "A": ["A1", "A1", "A1", "A2", "A2", "A2", "A3", "A3", "A3"],
    "B": ["B1", "B1", "B1", "B2", "B2", "B2", "B3", "B3", "B3"],
    "C": ["C1", "C1", "C2", "C2", "C2", "C3", "C3", "C3", "C3"],
    "D": [1, 2, 3, 4, 5, 6, 7, 8, 9],
})
cube = raw_df.pivot_table(
    index=["A", "B"],
    columns="C",
    values="D",
    aggfunc="sum",
    fill_value=0,
)
print(cube)
Сводная таблица выглядит так:
C       C1  C2  C3
A  B              
A1 B1   3   3   0
A2 B2   0   9   6
A3 B3   0   0  24
В чем сложность
С одноуровневым индексом можно использовать margins, но как только строки становятся MultiIndex, нужен способ посчитать и разместить итоги так, чтобы они соответствовали структуре индекса. Важно сохранить все в одной таблице, вставить групповые итоги на нужные позиции и обойтись без устаревших API.
Решение: groupby по уровню индекса, переоформление меток, concat и сортировка
Подход простой. Сначала агрегируем по нужному уровню индекса. Затем переименовываем результат так, чтобы он вернулся в MultiIndex как строка “Total” под каждой группой. В конце объединяем его с исходной сводной таблицей и сортируем по индексу.
# Промежуточные итоги по первому уровню индекса ("A")
level_sums = cube.groupby(level=0).sum().rename(index=lambda k: (k, "Total"))
with_totals = pd.concat([cube, level_sums]).sort_index()
print(with_totals)
В результате под каждой группой A появляется строка с промежуточным итогом:
C         C1  C2  C3
A1 B1      3   3   0
   Total   3   3   0
A2 B2      0   9   6
   Total   0   9   6
A3 B3      0   0  24
   Total   0   0  24
Если итоги нужны по второму уровню («B»), поменяйте уровень в groupby, соответствующим образом задайте новые метки индекса и отсортируйте по этому уровню:
# Промежуточные итоги по второму уровню индекса ("B")
level_sums_b = cube.groupby(level=1).sum().rename(index=lambda v: ("Total", v))
with_totals_b = pd.concat([cube, level_sums_b]).sort_index(level=1)
print(with_totals_b)
Получим:
C         C1  C2  C3
A1    B1   3   3   0
Total B1   3   3   0
A2    B2   0   9   6
Total B2   0   9   6
A3    B3   0   0  24
Total B3   0   0  24
Почему это важно
В отчетах промежуточные итоги помогают читабельности и быстрой проверке. Добавляя их прямо в сводную таблицу, вы сохраняете все в одном месте, избегаете ручного склеивания таблиц и корректно работаете с макетами на основе MultiIndex. Использование concat избавляет от устаревших приемов и совместимо с текущими версиями pandas.
Итоги
Если в pandas pivot_table с несколькими уровнями индекса нужны промежуточные итоги, посчитайте сумму по нужному уровню, подгоните метки так, чтобы они вписались в существующий MultiIndex, затем объедините и отсортируйте. Для одноуровневого индекса часто достаточно margins, а в многослойных сценариях этот прием дает аккуратный, готовый к презентации результат.
Статья основана на вопросе с StackOverflow от Thomas Petit и ответе от Aadvik.