Checkpoint: Analysis & Aggregation

Reviewed & published by Brayan K

This checkpoint is a hands-on review that combines every analysis skill from this section — binning, encoding, windows, group transforms, time-series math, and chaining — into one build challenge and a short self-test quiz.

Part of the free Pandas course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Work through the multi-step challenge first, check your answer against the full solution, then test your recall with the quiz before moving on to the capstone.

What You've Learned in This Section

1 Quick Recap — The Right Tool For The Job

Before the challenge, here is the mental map of when to reach for each tool you have met:

2 Warm-Up — Bin, Roll, Transform

A quick three-skill warm-up: bin a column with cut, add a rolling average, and broadcast a group mean with transform. Run it and read each new column.

# bin -> rolling -> transform, all on one frame
df["band"]   = pd.cut(df["sales"], bins=[0, 50, 100], labels=["lo", "hi"])
df["ma2"]    = df["sales"].rolling(2).mean()
df["b_mean"] = df.groupby("band", observed=True)["sales"].transform("mean")
import pandas as pd

df = pd.DataFrame({"day": [1, 2, 3, 4],
                   "sales": [30, 80, 40, 90]})

df["band"]   = pd.cut(df["sales"], bins=[0, 50, 100], labels=["lo", "hi"])
df["ma2"]    = df["sales"].rolling(2).mean()
df["b_mean"] = df.groupby("band", observed=True)["sales"].transform("mean")
print(df)

# ✅ Expected output:
#    day  sales band   ma2  b_mean
# 0    1     30   lo   NaN    35.0
# 1    2     80   hi  55.0    85.0
# 2    3     40   lo  60.0    35.0
# 3    4     90   hi  65.0    85.0

3 The Build Challenge — A Sales Analysis Pipeline

You are given a daily sales table per region. Build a single analysis that does all of the following, in order:

Try it in the editor first. The full reference solution is hidden just below — open it only after you have attempted the build.

import pandas as pd

df = pd.DataFrame({
    "day":    [1, 2, 3, 4, 5, 6],
    "region": ["N", "N", "N", "S", "S", "S"],
    "sales":  [40, 70, 55, 90, 30, 80],
})

# 1) bin sales at edge 60 -> "low" / "high"
# df["band"] = pd.cut(...)

# 2) 2-day rolling average -> df["roll2"]

# 3) within-region z-score -> df["z"]
#    def z(x): return (x - x.mean()) / x.std()

# 4) print total sales per region
# print(df.groupby("region")["sales"].sum())

print(df)

# ✅ Expected output:
#    day region  sales
# 0    1      N     40
# 1    2      N     70
# 2    3      N     55
# 3    4      S     90
# 4    5      S     30
# 5    6      S     80
import pandas as pd

df = pd.DataFrame({
    "day":    [1, 2, 3, 4, 5, 6],
    "region": ["N", "N", "N", "S", "S", "S"],
    "sales":  [40, 70, 55, 90, 30, 80],
})

# 1) bin sales into low / high
df["band"] = pd.cut(df["sales"],
                    bins=[0, 60, 200],
                    labels=["low", "high"])

# 2) 2-day rolling average
df["roll2"] = df["sales"].rolling(2).mean()

# 3) within-region z-score with transform
def z(x):
    return (x - x.mean()) / x.std()

df["z"] = df.groupby("region")["sales"].transform(z).round(3)

print(df)
#    day region  sales  band  roll2      z
# 0    1      N     40   low    NaN -1.000
# 1    2      N     70  high   55.0  1.000
# 2    3      N     55   low   62.5  0.000
# 3    4      S     90  high   72.5  0.726
# 4    5      S     30   low   60.0 -1.141
# 5    6      S     80  high   55.0  0.415

# 4) total sales per region
print(df.groupby("region")["sales"].sum())
# region
# N    165
# S    200
# Name: sales, dtype: int64

! Common Errors (And How to Fix Them)

❌ Assigning an agg result as a column

agg shrinks the frame and misaligns on assignment:

df["z"] = df.groupby("region")["sales"].mean()   # ❌ wrong shape

✅ Fix: use transform to keep the row count:

df["z"] = df.groupby("region")["sales"].transform("mean")   # ✅

❌ Rolling across groups by accident

df["roll2"] = df["sales"].rolling(2).mean()   # ❌ blends regions at the boundary

✅ Fix: roll inside each group when needed:

df.groupby("region")["sales"].rolling(2).mean()   # ✅

📝 Checkpoint Quiz

Answer each in your head, then expand to check.

pd.qcut. It splits on quantiles so every bin is equally full, unlike pd.cut which uses fixed value edges.

dtype=int. And drop_first=True removes the redundant first indicator column.

A full window of 3 is not available yet for the first two rows. Pass min_periods=1 to compute with partial windows instead.

transform returns the same shape as the input (one value per row, broadcast within each group), while agg collapses each group to a single summary row.

diff() is the absolute change in units; pct_change() is the relative change as a fraction (a 0.25 means +25%).

It returns all rows of the groups that pass the test. The function receives each group as a sub-DataFrame and must return a single boolean (True keeps the whole group).

❓ Frequently Asked Questions

Checkpoint cleared — you can analyse like a pro!

You combined binning, rolling windows, and group transforms into one pipeline, summarised by group, and tested your recall. That is the full analysis toolkit working together.

Practice quiz

Which function gives bins that each hold roughly the same number of rows?

  • pd.cut
  • pd.get_dummies
  • pd.qcut
  • df.rolling

Answer: pd.qcut. pd.qcut splits on quantiles so every bin is equally full. pd.cut uses fixed value edges, which can leave some bins nearly empty.

What argument makes pd.get_dummies return 0/1 integers instead of True/False?

  • dtype=int
  • drop_first=True
  • sparse=True
  • prefix='x'

Answer: dtype=int. dtype=int makes the indicator columns 0/1 integers. drop_first=True is a separate option that removes the redundant first indicator column.

Why are the first rows of a rolling(3).mean() NaN, and how can you fill them?

  • rolling always discards the first row
  • The data must be sorted first
  • You forgot to call .reset_index()
  • A full window of 3 is not available yet; pass min_periods=1 for partial windows

Answer: A full window of 3 is not available yet; pass min_periods=1 for partial windows. The first two rows do not have a full window of 3 values yet. Passing min_periods=1 lets pandas compute with partial windows instead of NaN.

How does groupby().transform differ from groupby().agg?

  • transform collapses each group to one row; agg keeps every row
  • transform returns the same shape as the input (one value per row); agg collapses each group to a single summary
  • They are identical
  • transform only works on strings

Answer: transform returns the same shape as the input (one value per row); agg collapses each group to a single summary. transform broadcasts a group statistic back onto every row, keeping the row count. agg collapses each group to a single summary row.

What is the difference between diff() and pct_change()?

  • diff is the absolute change in units; pct_change is the relative change as a fraction
  • diff is relative; pct_change is absolute
  • They both return percentages
  • diff only works on dates

Answer: diff is the absolute change in units; pct_change is the relative change as a fraction. diff() gives the absolute change in units, while pct_change() gives the relative change as a fraction, so 0.25 means a 25% increase.

What does groupby().filter() return, and what must its function output?

  • A single summary row; the function returns a number
  • Only the first row of each group; the function returns a string
  • All rows of the groups that pass the test; the function returns one boolean per group
  • A reshaped pivot table; the function returns a list

Answer: All rows of the groups that pass the test; the function returns one boolean per group. filter returns every row of the groups that pass. Its function receives each group as a sub-DataFrame and must return a single boolean — True keeps the whole group.

When you assign df['z'] = df.groupby('region')['sales'].mean(), what goes wrong?

  • Nothing, it works perfectly
  • agg shrinks the frame so the result misaligns by row; use transform instead
  • mean is not a valid method
  • It silently drops the region column

Answer: agg shrinks the frame so the result misaligns by row; use transform instead. agg/mean returns one value per group, not per row, so direct assignment misaligns. Use groupby('region')['sales'].transform('mean') to keep the row count.

Why pass observed=True when you groupby a categorical produced by pd.cut?

  • It sorts the groups alphabetically
  • It converts the result to integers
  • It is required for rolling windows
  • It keeps only the bins that actually appear, avoiding empty groups and warnings

Answer: It keeps only the bins that actually appear, avoiding empty groups and warnings. Grouping a categorical otherwise keeps every possible category, including empty bins. observed=True keeps only the bins that actually appear in the data.

Which window method weights recent observations more heavily than older ones?

  • .rolling
  • .ewm
  • .expanding
  • .cumsum

Answer: .ewm. .ewm computes an exponentially weighted mean, giving more weight to recent values. rolling treats every value in the window equally and expanding grows the window from the start.

What does df['sales'].shift(1) produce?

  • The cumulative sum of sales
  • The sales values sorted ascending
  • The sales values moved down one row, so each row sees the previous value
  • The percentage change of sales

Answer: The sales values moved down one row, so each row sees the previous value. shift(1) moves the column down by one row, so each row lines up with the previous period's value — the building block for diff and pct_change.

Continue this course

Frequently asked questions

What should I be able to do after this checkpoint?

You should be comfortable binning a numeric column, one-hot encoding categories, computing rolling and expanding windows, broadcasting group statistics with transform, filtering whole groups, deriving time-series changes with shift/diff/pct_change, and stitching it together with method chaining — then presenting the result cleanly.

Do I need to memorise every method signature?

No. The goal is to recognise which tool fits which problem: cut/qcut for binning, get_dummies for encoding, rolling/expanding for windows, transform/filter for groups, shift/diff/pct_change for time series. Looking up exact arguments is normal and expected.

Why combine transform and binning in one challenge?

Real analysis rarely uses a single method. Binning turns a continuous column into segments, and transform adds a within-segment statistic back onto every row, so the two together let you compare each record against its own bucket — a very common analytics pattern.

What comes after this checkpoint?

The capstone: an end-to-end data cleaning and analysis project that pulls together everything from the course — reading data, cleaning it, wrangling it, aggregating it, and presenting the result. This checkpoint is your warm-up for that.