Group Data with groupby Aggregations
Learn to group data with groupby aggregations in this step-by-step tutorial — covering the core concept, hands-on exercises, and common edge cases.
Focus: group data with groupby aggregations
You've cleaned your data, filtered rows, and created new columns — but now the real questions begin. How do you compare sales across regions? What's the average revenue per product category? Aggregate functions alone can't answer these; you need to group data with groupby aggregations. This is the step where raw tables become actionable insights, and mastering it will transform you from a data wrangler into a data analyst.
The problem this lesson solves
Imagine you have a DataFrame with thousands of rows: sales transactions, customer reviews, or sensor readings. You need to answer questions like "What is the average order value per customer?" or "How many units were sold per product?"
If you try to filter and calculate each group manually, you'll write repetitive, error-prone code that doesn't scale. Even worse, you're missing the forest for the trees — you can't see patterns across groups when you're stuck inspecting individual rows.
Without grouping, your analysis is limited to whole-table stats — you can't compare segments or trends.
Groupby aggregations solve this by letting you split your data into logical groups, apply a function (like sum, mean, count) to each group, and then combine the results into a tidy summary. This is the classic split-apply-combine pattern, and it's the backbone of countless real-world analyses.
Core concept / mental model
Think of grouping data like sorting a deck of cards by suit and then summarizing each pile. You're not looking at every card; you're asking, "How many hearts are there? What's the average value of spades?"
In pandas, the groupby method implements this idea:
- Split: The DataFrame is divided into groups based on one or more columns (your grouping keys).
- Apply: An aggregation function (e.g., sum, mean, count, max) is computed within each group.
- Combine: The results are assembled into a new DataFrame (or Series) with one row per group.
Key definitions: - Grouping key: The column(s) you use to divide the data. This can be a single column name or a list of columns. - Aggregation function: A statistical or summary operation that reduces multiple values into one — think total, average, or count. - Result: A new object where the grouping keys become the index (by default), and the aggregated columns are shown.
🧠 Pro tip: The result of a
groupbyis not a DataFrame — it's a specialDataFrameGroupByobject. You must apply an aggregation method to get your summary table.
How it works step by step
Here's the typical flow for grouping data with groupby aggregations:
- Choose your grouping key(s): Decide which column(s) define your groups. For example,
'Region'to compare regions, or['Year', 'Quarter']for time-based groupings. - Select the columns to aggregate: Sometimes you want all numeric columns; other times you specify just one or two.
- Pick your aggregation function: Common choices include
sum,mean,count,min,max,median,std, andfirst. You can also useagg()to apply multiple functions at once. - Call the method: Chain
df.groupby('key')['col'].sum()or usedf.groupby('key').agg({'col1': 'sum', 'col2': 'mean'}). - Inspect and use the result: The output is a new DataFrame indexed by your group keys. You can
reset_index()to turn it back into a normal column-based DataFrame for further analysis or plotting.
Why this order matters: Each step builds on the previous one. If you skip selecting columns, you'll aggregate all numeric columns, which may be noisy. If you forget to reset the index, you might accidentally treat the group keys as a row index in later steps.
Hands-on walkthrough
Let's put this into practice with a realistic sales dataset. Fire up your notebook and follow along.
import pandas as pd
# Sample sales data
df = pd.DataFrame({
'Region': ['North', 'South', 'North', 'East', 'South', 'East', 'North', 'East'],
'Product': ['A', 'A', 'B', 'A', 'B', 'B', 'A', 'A'],
'Units': [10, 5, 8, 12, 7, 6, 9, 4],
'Revenue': [1000, 700, 800, 1500, 900, 750, 1100, 500]
})
print(df)
Output:
Region Product Units Revenue
0 North A 10 1000
1 South A 5 700
2 North B 8 800
3 East A 12 1500
4 South B 7 900
5 East B 6 750
6 North A 9 1100
7 East A 4 500
Example 1: Total revenue per region
# Group by Region, sum the Revenue column
revenue_by_region = df.groupby('Region')['Revenue'].sum()
print(revenue_by_region)
Output:
Region
East 2750
North 2900
South 1600
Name: Revenue, dtype: int64
Example 2: Multiple aggregations on the same column
What if you want both total units and average revenue per product? Use the agg() method:
# Group by Product, compute sum of Units and mean of Revenue
summary = df.groupby('Product').agg({'Units': 'sum', 'Revenue': 'mean'})
print(summary)
Output:
Units Revenue
Product
A 40 950.0
B 21 816.7
Example 3: Aggregating multiple columns differently
Need a more complex report? Group by Region, then get total units and average revenue per region:
region_stats = df.groupby('Region').agg({
'Units': 'sum',
'Revenue': 'mean'
})
print(region_stats)
Output:
Units Revenue
Region
East 22 916.7
North 27 966.7
South 12 800.0
Example 4: Resetting the index for further use
Often you want the group keys as regular columns, not the index. Use reset_index():
region_stats_reset = df.groupby('Region', as_index=False)['Revenue'].sum()
print(region_stats_reset)
Output:
Region Revenue
0 East 2750
1 North 2900
2 South 1600
Alternatively, you can call reset_index() on the grouped result:
region_stats = df.groupby('Region')['Revenue'].sum().reset_index()
🧠 Pro tip: Use
as_index=Falseingroupbyto keep your grouping columns as regular columns right away — this often saves a step and prevents index-related surprises.
Compare options / when to choose what
There are several ways to aggregate after grouping, and each fits a different need:
| Method | Best for | Example |
|---|---|---|
Direct method (.sum(), .mean()) |
A single aggregation on one or all numeric columns | df.groupby('Region')['Revenue'].sum() |
agg() with a dict |
Different functions per column | df.groupby('Region').agg({'Units': 'sum', 'Revenue': 'mean'}) |
agg() with a list |
Multiple functions on the same column | df.groupby('Product')['Revenue'].agg(['sum', 'mean']) |
pivot_table |
Nice presentation with multi-level rows/columns | pd.pivot_table(df, values='Revenue', index='Region', aggfunc='sum') |
When to choose what:
- For quick, single summaries, use direct methods like sum() or mean().
- When you need different calculations on different columns, agg() with a dictionary is your friend.
- If you want multiple statistics (e.g., total and average) on the same column, pass a list to agg().
- For a more structured, spreadsheet-like output (especially with two grouping keys), consider pivot_table.
Troubleshooting & edge cases
-
KeyErrorwhen column name is wrong or missing - Double-check column names withdf.columns. Typos are the most common cause. -
Groupby with categorical data yields empty groups - If your grouping column is
category, groups that have no rows still appear withNaNor0. Useobserved=Trueingroupbyto include only observed categories. -
Aggregating non-numeric columns throws an error - Functions like
sum()expect numeric data. Either select numeric columns explicitly or use a function that works on strings (e.g.,first,count). -
Result index becomes the group key(s) unexpectedly - If you later try to merge or compare using column names, remember to
reset_index()or useas_index=False. -
NaN values in grouping columns - By default, pandas drops rows with
NAin the grouping key. If you need to keep them, usedropna=Falseingroupby.
What you learned & what's next
You've just unlocked the power of group data with groupby aggregations. You now understand the split-apply-combine pattern, can write foundational groupby calls, apply multiple aggregations, and avoid common pitfalls. These skills are essential for summarizing data fast and derive insights from your datasets.
In the next lesson, you'll dive into merging and joining datasets — combining multiple DataFrames into a single, richer table for even deeper analysis. With grouping and merging together, you'll be able to build complex, real-world analytical pipelines.
Keep practicing! Open any DataFrame, group by a meaningful column, and ask a question your data can answer. The more you experiment, the more natural these patterns will feel.
Practice recap
As a mini exercise, load a dataset of your choice (e.g., the built-in tips dataset from seaborn) and group by a categorical column like 'day' or 'sex'. Compute the average total_bill per group and then add the count of rows per group using agg(). Finally, reset the index and print the resulting DataFrame to see what you've built.
Common mistakes
- Forgetting to select a column before applying an aggregation (e.g.,
df.groupby('Region').sum()) — this aggregates all numeric columns, which may produce noisy or unintended results. - Expecting the grouped result to have group keys as columns — by default they become the index. Use
as_index=Falseorreset_index()to keep them as columns. - Trying to call
groupby.agg()without specifying column names when you need different functions per column — ensure your dictionary keys match existing column names exactly. - Ignoring NaN values in grouping columns, which are dropped by default — use
dropna=Falseif you need to keep them.
Variations
- Use
pivot_tablefor a more spreadsheet-like output, especially when you have two grouping keys:pd.pivot_table(df, values='Revenue', index='Region', columns='Product', aggfunc='sum'). - Apply multiple aggregation functions to the same column using a list:
df.groupby('Product')['Revenue'].agg(['sum', 'mean', 'count']). - For time-series data, use
pd.Grouperwith a frequency (e.g.,'M'for monthly) to group by date ranges:df.groupby(pd.Grouper(key='Date', freq='M')).sum().
Real-world use cases
- E-commerce: Calculate total sales and average order value per product category to guide inventory restocking decisions.
- Finance: Compute monthly total expenses and average transaction amount per spending category from a credit card statement.
- Operations: Analyze average response time and total tickets closed per support team member to optimize workload distribution.
Key takeaways
- The split-apply-combine pattern is the core of groupby aggregations: split data by keys, apply functions, and combine results.
- Use
df.groupby('key')['col'].func()for a quick, single aggregation on one column. - The
agg()method lets you apply different aggregation functions to multiple columns in one call. - Group keys become the index of the result by default — use
as_index=Falseorreset_index()to keep them as columns. - Handle edge cases: empty categories, NaN grouping keys, and non-numeric columns to avoid errors.
- Mastering groupby aggregations is a stepping stone to more advanced analyses like merging and joining datasets.