Tutorial 8: Data Aggregation and Grouping with Pandas

Introduction

In the previous chapter, we calculated overall statistics like mean, median, and correlation. Now we’ll learn to answer more practical, business-style questions: What’s the average salary by department? Which department has the highest total sales? How many employees are in each city? This is where grouping and aggregation become essential.

You’ll learn: groupby(), aggregation functions, multiple aggregations, grouping by multiple columns, pivot_table(), crosstab(), and how to answer business questions with grouped data.


1. Create a Sample Dataset

import pandas as pd

data = {
    "Employee": ["Amit", "Priya", "Rahul", "Sneha", "Vikram", "Anjali", "Kiran", "Neha"],
    "Department": ["IT", "HR", "Finance", "IT", "Sales", "Finance", "IT", "HR"],
    "City": ["Mumbai", "Delhi", "Mumbai", "Pune", "Delhi", "Mumbai", "Pune", "Delhi"],
    "Salary": [75000, 60000, 85000, 90000, 65000, 95000, 80000, 70000],
    "Experience": [5, 3, 7, 8, 4, 10, 6, 5]
}

df = pd.DataFrame(data)
df

2. What is Aggregation?

Aggregation means summarizing many rows into a smaller number of meaningful values.

Individual Employees → GROUP by Department → AGGREGATE → Average Salary

Example result:

DepartmentAverage Salary
Finance₹90,000
HR₹65,000
IT₹81,667
Sales₹65,000

3. Basic groupby()

df.groupby("Department")

This creates a “grouped object” — it doesn’t show results yet; it’s a plan waiting for an aggregation to be applied.

Calculate average salary:

df.groupby("Department")["Salary"].mean()

Output:

Department
Finance    90000
HR         65000
IT         81667
Sales      65000

Interpretation: Finance has the highest average salary in this dataset.


4. Count Records by Group

df.groupby("Department")["Employee"].count()

Or, equivalently:

df["Department"].value_counts()

Both give the same answer — which one to use often comes down to whether you’re already mid-chain on a groupby() or working with the column directly.


5. Common Aggregation Functions

FunctionPurpose
count()Count values
sum()Total
mean()Average
median()Middle value
min() / max()Minimum / maximum
std()Standard deviation

Example:

df.groupby("Department")["Salary"].max()

6. Multiple Aggregations with agg()

Get minimum, maximum, and average salary all at once:

df.groupby("Department")["Salary"].agg(["min", "max", "mean"])

Example output:

Departmentminmaxmean
Finance850009500090000
HR600007000065000
IT750009000081667
Sales650006500065000

7. Named Aggregations

Make the output more readable with custom column names:

df.groupby("Department").agg(
    Average_Salary=("Salary", "mean"),
    Maximum_Salary=("Salary", "max"),
    Employee_Count=("Employee", "count")
)

This is particularly useful for building business reports, since the column names in the output become self-explanatory rather than generic.


8. Grouping by Multiple Columns

Group by Department and City together:

df.groupby(["Department", "City"])["Salary"].mean()

This produces a more detailed, two-level breakdown — for instance, IT employees in Mumbai vs. IT employees in Pune, rather than just “IT” as a whole.


9. Reset the Index

Grouped results often come back with a “hierarchical index” (multiple levels stacked). Flatten it back into a normal table:

result = (
    df.groupby(["Department", "City"])["Salary"]
    .mean()
    .reset_index()
)
result

10. Business Questions Using groupby()

Which department has the highest average salary?

avg_salary = df.groupby("Department")["Salary"].mean()
avg_salary.idxmax()

What’s the highest salary in each department?

df.groupby("Department")["Salary"].max()

How many employees work in each city?

df.groupby("City")["Employee"].count()

What’s the average experience by department?

df.groupby("Department")["Experience"].mean()

11. Pivot Tables

A pivot table summarizes data using rows, columns, and values — like a spreadsheet pivot table (in fact, that’s exactly what inspired the name).

Calculate average salary by department and city:

pd.pivot_table(
    df,
    values="Salary",
    index="Department",
    columns="City",
    aggfunc="mean"
)

12. Pivot Table with Multiple Aggregations

pd.pivot_table(
    df,
    values="Salary",
    index="Department",
    aggfunc=["mean", "max", "min"]
)

13. Pivot Table with Multiple Values

pd.pivot_table(
    df,
    values=["Salary", "Experience"],
    index="Department",
    aggfunc="mean"
)

This gives, for each department, both the average salary and the average experience side-by-side.


14. Cross-Tabulation with crosstab()

pd.crosstab() is primarily used to count combinations of two categorical variables — how many employees are in each department/city combination?

pd.crosstab(df["Department"], df["City"])

15. Cross-Tabulation with Percentages

pd.crosstab(df["Department"], df["City"], normalize="index")

This answers: what percentage of each department’s employees work in each city?


16. Add Margins

pd.crosstab(df["Department"], df["City"], margins=True)

margins=True adds row totals, column totals, and a grand total.


17. Visualize Aggregated Data

avg_salary = df.groupby("Department")["Salary"].mean()

import matplotlib.pyplot as plt

avg_salary.plot(kind="bar")
plt.title("Average Salary by Department")
plt.xlabel("Department")
plt.ylabel("Average Salary")
plt.show()

Or with Seaborn:

import seaborn as sns

sns.barplot(data=df, x="Department", y="Salary")
plt.title("Average Salary by Department")
plt.show()

18. Complete Business Analysis Example

avg_salary = df.groupby("Department")["Salary"].mean()
employee_count = df["Department"].value_counts()
max_salary = df.groupby("Department")["Salary"].max()
avg_experience = df.groupby("Department")["Experience"].mean()

print("Average Salary:")
display(avg_salary)

print("Employee Count:")
display(employee_count)

print("Maximum Salary:")
display(max_salary)

print("Average Experience:")
display(avg_experience)

19. Create a Department Summary

Combine everything into one clean summary table:

department_summary = (
    df.groupby("Department")
    .agg(
        Employee_Count=("Employee", "count"),
        Average_Salary=("Salary", "mean"),
        Maximum_Salary=("Salary", "max"),
        Average_Experience=("Experience", "mean")
    )
    .reset_index()
)

department_summary

This is a classic business-ready summary table — exactly the kind of output you’d paste into a report or dashboard.


groupby() vs. pivot_table() vs. crosstab()

ToolBest Used For
groupby()Flexible grouping and aggregation
pivot_table()Spreadsheet-style summaries
crosstab()Counting combinations of categories

Simple decision guide: Need statistics by group? → groupby(). Need a table with rows and columns? → pivot_table(). Need category counts? → crosstab().


Practice Exercise

Using the Titanic dataset, calculate: average age by passenger class, survival rate by gender, and average fare by passenger class. Then build a pivot table of survival rate by gender and class, and a cross-tabulation of gender vs. class.

Key Takeaway: Data aggregation transforms detailed row-level data into meaningful summaries that help us understand patterns and answer business questions.

Scroll to Top