Unit 8 • Lesson 2

Merging on a Key

Overview

Bringing two tables together on the column they share.

Intermediate 20–25 min

What You Will Learn in This Lesson

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

  • Join two tables on a shared column.
  • Choose between inner, left, right and outer.
  • Say what happens to a row that matches nothing.
  • Check the row count against what you expected.

Why This Matters

A join is how two files become one answer, and the choice of how is a decision about whose rows matter. Getting it wrong does not raise — it silently drops the people you were asked about, and the summary that follows is computed over the wrong population.

Step 1: An inner join keeps the matches

The default keeps only the rows whose key appears in both tables.

1

Joining on a shared column

The key column appears once in the result; the other columns come from both sides.

import pandas as pd

people = pd.DataFrame({"id": [1, 2], "name": ["Ada", "Bo"]})
scores = pd.DataFrame({"id": [1], "score": [92]})
print(pd.merge(people, scores, on="id"))
2

What an inner join costs you

Bo is gone. If the question was "what did each person score", an inner join has quietly answered a different one: "what did each person who has a score score".

import pandas as pd

people = pd.DataFrame({"id": [1, 2], "name": ["Ada", "Bo"]})
scores = pd.DataFrame({"id": [1], "score": [92]})
inner = pd.merge(people, scores, on="id")
print(len(people), "people ->", len(inner), "rows")

Mini Practice #1: Inner against left

Try It Yourself

Join these two both ways and print the row count each gives.

Press Run to see output

Step 2: how= decides what happens to the rest

Four choices, and the right one depends on whose rows the question is about.

1

left keeps everyone on the left

Rows with no match get NaN in the columns that came from the right. This is the one you want when the left table is the population you were asked about.

import pandas as pd

people = pd.DataFrame({"id": [1, 2], "name": ["Ada", "Bo"]})
scores = pd.DataFrame({"id": [1], "score": [92]})
print(pd.merge(people, scores, on="id", how="left"))
2

And the other two

right is the mirror image; outer keeps everything from both sides and fills the gaps on each.

import pandas as pd

a = pd.DataFrame({"id": [1, 2], "x": ["a", "b"]})
b = pd.DataFrame({"id": [2, 3], "y": ["c", "d"]})
print(pd.merge(a, b, on="id", how="outer"))
3

Counting what did not match

After a left join, the rows with NaN in a right-hand column are exactly the ones that found no partner. That number is usually worth reporting, not just fixing.

import pandas as pd

people = pd.DataFrame({"id": [1, 2, 3], "name": ["a", "b", "c"]})
scores = pd.DataFrame({"id": [1], "score": [92]})
out = pd.merge(people, scores, on="id", how="left")
print(int(out["score"].isna().sum()), "of", len(people), "had no score")

Mini Practice #2: Who is missing

Try It Yourself

Left join, then print the names of the people who had no score.

Press Run to see output

Step 3: When to Use It

Adding columns from another table

Names from a people table onto a scores table, matched on the id they share.

pd.merge(scores, people, on="id", how="left")

Choosing which rows survive

inner keeps matches only; left keeps every row of the main table.

When Not to Use It

Do not merge without checking the row count before and after. An inner join drops unmatched rows silently, and duplicate keys multiply them.

End-of-Lesson Exercises

Exercise 1: Keep everyone, count the gaps

Write with_scores(people, scores) that joins on id, keeping every person even when they have no score, and returns the number of people with no score.

how="left" keeps every person. The people who matched nothing have a missing score, and isna().sum() counts them.

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

Exercise 2: Inner against left, as a pair

Write join_counts(left, right) returning a two-element list of [rows in an inner join on id, rows in a left join]. The pair is what tells you whether every left row found a partner.

Two merges, one with the default how and one with how="left", and len() on each. When the two numbers differ, the difference is how many left rows matched nothing.

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