Skip to content
AgileLogics
6 min read

Joining Data

Prerequisites: dplyr: mutate, summarise, and group_by

Data often lives in several tables: orders in one, customers in another. A join combines them by matching a key column that both share.

library(dplyr, warn.conflicts = FALSE)

customers <- tibble(
  customer_id = c(1, 2, 3),
  name = c("Ada", "Linus", "Grace")
)

orders <- tibble(
  order_id = c(101, 102, 103, 104),
  customer_id = c(1, 1, 3, 4),
  total = c(120, 45, 300, 80)
)

orders |> left_join(customers, by = "customer_id")
# A tibble: 4 × 4
  order_id customer_id total name
     <dbl>       <dbl> <dbl> <chr>
1      101           1   120 Ada
2      102           1    45 Ada
3      103           3   300 Grace
4      104           4    80 <NA>

left_join keeps every row of the left table (orders), adding matching columns from the right. Order 104 belongs to customer 4, who isn't in customers, so name is NA.

The main joins

library(dplyr, warn.conflicts = FALSE)

customers <- tibble(customer_id = c(1, 2, 3), name = c("Ada", "Linus", "Grace"))
orders <- tibble(order_id = c(101, 102, 103, 104), customer_id = c(1, 1, 3, 4))

inner_join(orders, customers, by = "customer_id")
anti_join(customers, orders, by = "customer_id")
semi_join(customers, orders, by = "customer_id")
# A tibble: 3 × 3
  order_id customer_id name
     <dbl>       <dbl> <chr>
1      101           1 Ada
2      102           1 Ada
3      103           3 Grace
# A tibble: 1 × 2
  customer_id name
        <dbl> <chr>
1           2 Linus
# A tibble: 2 × 2
  customer_id name
        <dbl> <chr>
1           1 Ada
2           3 Grace
Join Keeps
left_join all rows of the left table
inner_join only rows with a match in both tables
full_join all rows of both tables
anti_join left rows with no match — "customers who never ordered"
semi_join left rows with a match, without adding columns

When key columns have different names, use by = join_by(customer_id == id).

Stacking tables

To combine tables with the same columns (for example, one file per month), stack them with bind_rows().

Common mistakes

  • Duplicate keys multiply rows. If a customer appears twice in the right table, each of their orders appears twice after the join. dplyr warns about unexpected many-to-many matches — always check the row count before and after joining.
  • Keys with different types (number in one table, text in the other) don't match. Convert them first.