Page 6 of 8~113 min topic

NumPy and pandas basics

Grouping and aggregating

"Average score, by class" is a two-step idea: split by group, then summarize each group — pandas calls this `groupby`.

~14 min this pagePractical application

1Learn the idea

Teach

The split-apply-combine pattern

groupby works in three conceptual steps, even though you usually write it as one line:

  1. Split — divide the DataFrame into groups based on a column's values (e.g., one group per unique class)
  2. Apply — compute something for each group independently (a mean, a count, a max)
  3. Combine — collect the per-group results back into a single table
import pandas as pd

df = pd.DataFrame({
    "class": ["A", "A", "B", "B", "B"],
    "score": [88, 92, 79, 95, 61],
})

print(df.groupby("class")["score"].mean())
class
A    90.0
B    78.333333
Name: score, dtype: float64

Read

Common aggregations

Beyond .mean(), the same groupby(...)[...] pattern works with several other summaries — and you can request several at once with .agg():

df.groupby("class")["score"].count()   # how many students per class
df.groupby("class")["score"].max()     # top score per class
df.groupby("class")["score"].agg(["mean", "max", "count"])  # all three together

.agg([...]) is worth knowing early — it avoids writing three nearly identical lines when you want three related summaries side by side.

Read

Grouping by more than one column

Real questions are often two-dimensional: "average score, by class and by term." Passing a list of column names groups by their combination:

df.groupby(["class", "term"])["score"].mean()

Read

Turning a groupby result back into a flat table

A groupby result sometimes needs to go back into a normal, flat DataFrame — for example, to merge it back with the original data or plot it. .reset_index() does exactly that:

summary = df.groupby("class")["score"].mean().reset_index()
print(summary)
  class      score
0     A  90.000000
1     B  78.333333

Without .reset_index(), class stays as the table's index rather than an ordinary column — usually fine for a quick look, but often inconvenient once you want to combine this result with something else.

Read

Where this connects to earlier pages

Grouping and boolean masking solve related but different problems, and combining them is common: "average score, by class, for students who studied more than 3 hours" is a mask (df[df["hours"] > 3]) followed by a groupby on the filtered result — the two techniques compose naturally rather than competing.

Read

Sorting a grouped summary

A grouped summary is only as useful as how easily you can read the extremes off it — combining groupby with sort_values from the previous page answers "which group is doing best/worst" in one line:

df.groupby("class")["score"].mean().reset_index().sort_values("score", ascending=False)

Reading this left to right mirrors how you'd describe the question out loud: group by class, take the average score, turn it back into a normal table, then sort so the strongest class is on top. Building comfort with chaining several of these small, well-understood steps together — rather than writing one dense unreadable line from scratch — is a core pandas habit, and it's exactly what the worked example on the next page does with a slightly messier, more realistic dataset.

Go deeper

Before you start

Why this matters

Filtering (from an earlier page) answers "which rows match a condition." Grouping answers a different, equally common question: "how does this number break down by category?" — average score per class, total sales per region, count of errors per day. This is the same mental move as a spreadsheet pivot table, expressed in code.

In the wild

See how this idea shows up as a product and a company — then come back to the lesson. Skills transfer across vendors.

Continue learning · glossary & guides