Customers are between 18 and 80 years old. Grouping them by exact age gives dozens of tiny groups, so instead you sort the ages into a few ranges, called buckets, and compare the buckets.
pd.cut(column, bins=..., labels=...) does the sorting. bins is a list of edges: each bucket runs from above one edge up to and including the next one, so 5 edges make 4 buckets. labels names the buckets, in order. pd.cut gives back a column with the label of each value's bucket, and NaN (missing) where the value itself is missing. For example, on spending:
levels = pd.cut(df["spent"], bins=[0, 100, 300, 1000], labels=["low", "mid", "high"])
# 80 → low, 100 → low, 150 → mid, 450 → high
The new column works like any other: groupby it and summarize each bucket with agg, where each name=(column, summary) makes one column of the result. Rows whose bucket is NaN are left out of the groups. .isna() gives back True where a value is missing, and .sum() counts the Trues.
Your script must print:
age age_group
0 52.0 45-59
1 41.0 30-44
2 51.0 45-59
3 24.0 18-29
4 61.0 60-80
customers avg_spent returned
age_group
18-29 75 177.333 0.067
30-44 136 193.529 0.147
45-59 69 195.087 0.087
60-80 6 136.667 0.167
14 customers have no age, so no group: the groups hold 286 of 300 customers.
Your task: cut age into the groups 18-29, 30-44, 45-59 and 60-80 in a new age_group column and print the first 5 rows of both columns. Then print each group's number of customers, average spent and share who returned an item, and a sentence about the customers left without a group.
Assignment
Follow the Coding Tutorial to cut the ages into four buckets with pd.cut, summarize each age group and count the customers left out.
Hint
The first bucket of bins=[17, 29, 44, 59, 80] is above 17, up to and including 29: that's why its label is 18-29. The mean of a 0/1 column like returned_item is the share of 1s.
Free plan: 30 tutor messages. PRO is unlimited.
