Multiple Grouped Summaries in Pandas – Advanced GroupBy Techniques 2026

Data Manipulation Mar 18, 2026

Multiple Grouped Summaries in Pandas – Advanced GroupBy Techniques 2026

When you need to calculate several different summary statistics across multiple groups and columns, Pandas offers powerful and elegant solutions. In 2026, using groupby() combined with named aggregation in .agg() is the cleanest, most readable, and most performant way to create complex multi-group summaries.

TL;DR — Recommended Pattern

  • Use groupby([col1, col2]) with named aggregation
  • Apply different functions to different columns in one operation
  • Use method chaining for clarity
  • Combine with date components for time-based grouping

1. Basic Multiple Grouped Summaries

import pandas as pd

# Create a small demo file so this example runs as-is
# (in your project, sales_data.csv is your real export)
import numpy as np
rng = np.random.default_rng(42)
n = 60
pd.DataFrame({
    "order_date": pd.date_range("2025-10-01", periods=n, freq="3D"),
    "customer_id": rng.integers(101, 121, n),
    "customer_name": rng.choice([" ana", "ben ", "chen", "dana"], n),
    "product_id": rng.integers(1, 9, n),
    "region": rng.choice(["North", "South", "East", "West"], n),
    "category": rng.choice(["Electronics", "Books", "Home"], n),
    "payment_method": rng.choice(["card", "cash", "wallet"], n),
    "segment": rng.choice(["consumer", "business"], n),
    "quantity": rng.integers(1, 6, n),
    "amount": rng.uniform(50, 2500, n).round(2),
}).assign(
    ship_date=lambda d: d["order_date"] + pd.Timedelta(days=2),
    status=lambda d: np.where(d["amount"] > 2000, "priority", "standard"),
).to_csv("sales_data.csv", index=False)

df = pd.read_csv("sales_data.csv", parse_dates=["order_date"])

summary = (
    df
    .groupby(["region", "category"])
    .agg(
        total_sales=("amount", "sum"),
        avg_sale=("amount", "mean"),
        order_count=("amount", "count"),
        unique_customers=("customer_id", "nunique"),
        total_quantity=("quantity", "sum")
    )
    .round(2)
    .reset_index()
)

print(summary)

2. Advanced Multi-Level Grouped Summaries with Dates

monthly_report = (
    df
    .groupby([
        "region",
        df["order_date"].dt.to_period("M").rename("month"),
        "category"
    ])
    .agg(
        total_sales=("amount", "sum"),
        average_order_value=("amount", "mean"),
        transaction_count=("amount", "count"),
        unique_customers=("customer_id", "nunique"),
        max_sale=("amount", "max"),
        min_sale=("amount", "min")
    )
    .round(2)
    .reset_index()
)

print(monthly_report)

3. Using Custom Functions in Multiple Grouped Summaries

def cv(x):
    """Coefficient of variation"""
    return x.std() / x.mean() if x.mean() != 0 else 0

report = (
    df
    .groupby(["region", "category"])
    .agg(
        total_revenue=("amount", "sum"),
        avg_revenue=("amount", "mean"),
        revenue_volatility=("amount", cv),
        customer_count=("customer_id", "nunique"),
        product_diversity=("product_id", "nunique")
    )
    .round(2)
)

4. Best Practices in 2026

  • Use named aggregation syntax (new_name=("column", "func")) for clear, meaningful column names
  • Group by multiple columns including date components using .dt.to_period()
  • Always use method chaining to keep complex aggregations readable
  • Round results and use .reset_index() for clean output
  • Define small custom functions when you need special calculations

Working with Multi-Level Results

When you request several statistics for several columns, pandas returns MultiIndex columns such as ("amount", "sum"). They are powerful for exploring but awkward for exports and charts. You can either use named aggregation from the start or flatten the columns afterwards by joining the two levels with an underscore. Flat names also make later steps like merge() and query() simpler.

Grouped results can also be compared with the overall figure by using transform(), which returns a value for every original row. For example, df["amount"] / df.groupby("region")["amount"].transform("sum") gives each order's share of its region total.

Worked Example: Flattening Column Names

This example creates multi-level columns and converts them into simple names.

import pandas as pd

df = pd.DataFrame({
    "region": ["East", "East", "West", "West"],
    "amount": [10.0, 30.0, 20.0, 40.0],
    "quantity": [1, 3, 2, 2],
})

res = df.groupby("region").agg({"amount": ["sum", "mean"], "quantity": ["sum"]})
res.columns = ["_".join(col) for col in res.columns]
print(res.reset_index())
#   region  amount_sum  amount_mean  quantity_sum
# 0   East        40.0         20.0             4
# 1   West        60.0         30.0             4

The flat names work with to_csv(), SQL exports and plotting libraries without further changes.

Common Mistakes

  • Indexing MultiIndex columns by a single name: res["sum"] fails; use tuples or flatten
  • Using apply() for simple statistics: agg() is clearer and faster
  • Repeating groupby() for each metric: compute them together
  • Forgetting reset_index() before saving, which leaves the group keys hidden in the index of the exported file

Quick Summary

  • Several statistics produce MultiIndex columns
  • Flatten with "_".join or use named aggregation
  • Calculate every metric in one grouped call

Conclusion

Creating multiple grouped summaries is one of the most powerful capabilities in Pandas. In 2026, the combination of groupby() with named aggregation allows you to generate rich, insightful reports with very clean and maintainable code. Whether you are analyzing sales by region and month, user behavior by category, or any multi-dimensional data — this pattern will serve you extremely well.

Next steps:

  • Take one of your current datasets and create a multi-column, multi-group summary using named aggregation

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices