Skip to content

Beginner: data foundations · Cleaning, exploring and visualising data · topic 19 of 62 · 35 min

Grouping, joining and reshaping

Explanation

Grouping answers "per what?" questions: total sales per city, average marks per class. Joining combines two tables on a shared key, like orders and customers. Reshaping turns long tables into summary grids (pivot tables). pandas does these with groupby, merge and pivot_table.

Split, apply, combine

Most business questions are about groups. "Which city sells the most?" means: split the rows by city, apply a sum to each group, and combine the answers into one small table. This pattern is called split-apply-combine, and in pandas it is df.groupby("city")["amount"].sum().

python
sales = pd.DataFrame({
    "city": ["Pune", "Delhi", "Pune", "Mumbai", "Delhi"],
    "amount": [450, 1200, 780, 2100, 150],
})
print(sales.groupby("city")["amount"].sum())
# city
# Delhi     1350
# Mumbai    2100
# Pune      1230
# Name: amount, dtype: int64
The groups come out sorted by name by default.

Several answers at once with agg

python
sales.groupby("city")["amount"].agg(["count", "sum", "mean"])
#         count   sum    mean
# city
# Delhi       2  1350   675.0
# Mumbai      1  2100  2100.0
# Pune        2  1230   615.0
agg takes a list of summary functions and gives one column per function.

Joining tables with merge

Data is often split across tables. Orders have a customer_id; the customers table has the name and city for each id. pd.merge(orders, customers, on="customer_id", how="left") brings them together. This is exactly a SQL JOIN.

how=KeepsSQL name
"inner"Only rows with a match in both tablesINNER JOIN
"left"All rows of the left table; missing matches become NaNLEFT JOIN
"right"All rows of the right tableRIGHT JOIN
"outer"All rows from bothFULL OUTER JOIN
Careful: If the key is repeated in BOTH tables, a merge creates every combination, and your row count can explode. Check len(df) before and after every merge, and use validate="many_to_one" to make pandas raise an error if the right table has repeated keys.

Pivot tables

python
orders.pivot_table(index="city", columns="month", values="amount", aggfunc="sum", fill_value=0)
#        Jan   Feb   Mar
# Delhi  1200  0     150
# Pune   450   780   0
The same pivot table you know from Excel: rows by city, columns by month, cells are sums.

Check every result

After a groupby or merge, always run two quick checks. First, do the totals still add up? The sum of the per-city totals must equal the sum of the whole column. Second, does the row count make sense? A left merge must have at least as many rows as the left table, and exactly as many when the right-hand keys are unique. These checks take seconds and catch most join and grouping bugs before your numbers reach a manager.

To group by more than one column, pass a list: df.groupby(["city", "month"])["amount"].sum() gives one row per city and month. Add .reset_index() to turn the group labels back into normal columns, which is easier to save or merge later.

The same idea in plain Python

python
from collections import defaultdict

totals = defaultdict(int)
for city, amount in [("Pune", 450), ("Delhi", 1200), ("Pune", 780)]:
    totals[city] += amount
print(dict(totals))
Shows {'Pune': 1230, 'Delhi': 1200}. A groupby is a dictionary of running totals.

Common mistakes

  • Grouping by a column with messy spellings, so "Pune" and "pune" become two groups. Fix: clean first.
  • Using an inner join by accident and silently losing customers with no orders. Fix: think about which rows you must keep; often you want a left join.
  • Averaging averages: the mean of city averages is not the overall mean when cities have different numbers of orders. Fix: compute from the raw rows or weight by count.
  • Forgetting that groupby drops rows where the key is missing (NaN) by default. Fix: fill the key first or pass dropna=False.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Grouping, joining and reshaping · 1/7

Group · join · pivot

This lesson is about answering "per what" questions: grouping, joining and pivoting.

Prefer a video by a teacher? Find free videos on “pandas groupby merge pivot table tutorial” on YouTube ↗

In real life: Which restaurant partners need help?

A food-delivery company keeps orders in one table and restaurants in another. An analyst joins them on restaurant_id (left join from restaurants, so restaurants with zero orders still appear), groups by city and cuisine, and computes order count, average delivery time and cancellation rate. Restaurants with no orders this month and those with high cancellation rates are sent to the partner team for follow-up.

Practice (0/3 done)

Task 1 · Code · Python

Write group_sum(rows, key, value) where rows is a list of dicts. Return a dict mapping each distinct rows[i][key] to the sum of rows[i][value] for that group (like df.groupby(key)[value].sum()). Example: group_sum([{"city": "Pune", "amount": 450}, {"city": "Pune", "amount": 780}], "city", "amount") returns {"Pune": 1230}.

Task 2 · Code · Python

Write group_mean(rows, key, value) that returns a dict mapping each group to the AVERAGE of value in that group, rounded to 2 decimal places.

Task 3 · Code · Python

Write left_join(left, right, key) for two lists of dicts. For each row in left (in order), find the row in right with the same key and return a merged dict {**left_row, **right_row}. If there is no match, keep the left row as it is (a left join). Assume keys in right are unique.

Quick quiz

Question 1

1.What does df.groupby("city")["amount"].sum() return?

Question 2

2.You merge customers (left) with orders using how="left". What happens to customers with no orders?

Question 3

3.After a merge, your 1,000 orders became 3,400 rows. What is the likely cause?

Question 4

4.City A has 2 orders averaging ₹100, city B has 8 orders averaging ₹200. What is the overall average order?

Question 5

5.Which tool makes an Excel-style grid with cities as rows and months as columns?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does df.groupby("city")["amount"].sum() return?

Answer: The total amount for each city

Split by city, sum each group, combine into one value per city.

You merge customers (left) with orders using how="left". What happens to customers with no orders?

Answer: They are kept, with missing (NaN) order columns

A left join keeps every row of the left table.

After a merge, your 1,000 orders became 3,400 rows. What is the likely cause?

Answer: The key was repeated in both tables, creating many combinations

Many-to-many keys multiply rows. Check the keys are unique on one side.

City A has 2 orders averaging ₹100, city B has 8 orders averaging ₹200. What is the overall average order?

Answer: ₹180

Total = 2 × 100 + 8 × 200 = 1,800 over 10 orders = ₹180. Averaging the averages (₹150) is wrong.

Which tool makes an Excel-style grid with cities as rows and months as columns?

Answer: pivot_table

pivot_table reshapes long data into a grid of summaries.

After customers LEFT JOIN orders, which count shows 0 for a customer with no orders?Related

Answer: COUNT(o.id)

COUNT(column) skips NULLs. The customer's single row has o.id = NULL, so COUNT(o.id) is 0 while COUNT(*) is 1.

From GROUP BY and HAVING

What does SELECT city, SUM(amount) FROM orders GROUP BY city return?Related

Answer: One row per city with its total

GROUP BY city makes one group per city, and SUM runs on each group.

From Counting and totals: aggregate functions

Why is a customer's city stored in a customers table instead of on every order row?Related

Answer: So it is stored once and a change happens in one place

Storing a fact once avoids copies that can disagree. Orders refer to the customer by id.

From Joins: combining tables

customers LEFT JOIN orders ... JOIN order_items (an inner join). What happens to Arjun, who has no orders?Related

Answer: He is dropped by the inner join, because his NULL order id matches no order line

A later inner join removes the unmatched rows the LEFT JOIN kept. Use LEFT JOIN for the later joins too.

From Joining three or more tables

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Cleaning, exploring and visualising data” before its exam: Exploratory data analysis (EDA), Visualising data honestly.
  2. Module 4 exam: Cleaning, exploring and visualising data · 12 questions · 25 min, without help. 6 topics of this module still to finish first.
  3. Then the next module, The ML workflow, regression and classification. 62 topics left in the course before the final exam.

← Previous: Cleaning messy data