Unit 7 • Lesson 5

transform versus aggregate

Overview

One answer per group, or one answer per row.

Intermediate 20–25 min

What You Will Learn in This Lesson

By the end of this lesson, you will be able to:

  • Say what shape an aggregate returns and what shape a transform returns.
  • Put a group summary beside every row.
  • Compare a row with its own group rather than with the table.
  • Choose between the two from the question being asked.

Why This Matters

Questions like "which rows beat their own group average" cannot be answered by an aggregate, because an aggregate throws away the rows. transform is what keeps the shape, and it is the piece people are missing when they fall back to a loop over the groups.

Step 1: aggregate collapses

One value per group. The rows are gone, which is usually what you wanted.

1

The shape of an aggregate

Four rows in, two groups out. Nothing here lines up with the original table any more.

import pandas as pd

df = pd.DataFrame({"k": ["a", "a", "b", "b"], "v": [1, 3, 10, 20]})
means = df.groupby("k")["v"].mean()
print(means)
print(len(df), "rows ->", len(means), "groups")

Mini Practice #1: Two shapes

Try It Yourself

Print the length of the aggregate and the length of the transform, next to the length of the table.

Press Run to see output

Step 2: transform keeps the shape

One value per row — each row gets its own group's answer. The result lines up with the table, so you can put it straight into a column or compare against it.

1

The group answer, beside every row

Same length as the table, same index, so it can sit alongside the data it describes.

import pandas as pd

df = pd.DataFrame({"k": ["a", "a", "b", "b"], "v": [1, 3, 10, 20]})
df["group_mean"] = df.groupby("k")["v"].transform("mean")
print(df)
2

Comparing a row with its own group

This is the question an aggregate cannot answer. Note how different it is from comparing against the whole table's mean.

import pandas as pd

df = pd.DataFrame({"k": ["low", "low", "high"], "v": [1, 2, 100]})
group_mean = df.groupby("k")["v"].transform("mean")

print("above own group:", int((df["v"] > group_mean).sum()))
print("above table mean:", int((df["v"] > df["v"].mean()).sum()))

The row with 100 is above the table mean and is exactly its own group's mean, because it is alone in its group. Which of those two numbers you report is the difference between two quite different claims.

3

Standardising within a group

The same idea with two transforms: each value's distance from its group mean, in units of its group's spread.

import pandas as pd

df = pd.DataFrame({"k": ["a", "a", "a", "b"], "v": [10.0, 20.0, 30.0, 5.0]})
g = df.groupby("k")["v"]
df["z"] = (df["v"] - g.transform("mean")) / g.transform("std")
print(df)

Mini Practice #2: Beat your own group

Try It Yourself

Add a column holding each row's group mean, then count the rows above it. Compare that with the count above the whole table's mean.

Press Run to see output

Step 3: When to Use It

Comparing each row to its group

transform returns one value per row, aligned to the original table.

df["vs_team"] = df["score"] - df.groupby("team")["score"].transform("mean")

Filling gaps with a group value

Fill missing values with the mean of the row’s own group rather than the whole column.

When Not to Use It

Do not use agg when you need a value on every row, or transform when you want one row per group. The shape of the answer decides which.

End-of-Lesson Exercises

Exercise 1: Rows above their own group

Write above_group_mean(df) returning the number of rows whose v is above the mean of their own group.

transform("mean") gives each row its group average, which lines up with the table so you can compare row by row. An aggregate would collapse the groups and leave you nothing to compare against.

Press Run to try it, then Check my work to mark it.

Exercise 2: Each row as a share of its group

Write share_of_group(df) returning a list of each row's v as a percentage of its own group's total, rounded to one decimal place, in the original row order.

transform("sum") gives each row its group total, lined up with the table. Divide, multiply by 100, round, and tolist().

Press Run to try it, then Check my work to mark it.