How to merge data frames in R
By the Analistable team · Updated · 2 min read
In base R, merge(x, y, by = "id") does an inner join; add all.x = TRUE for a left join or all = TRUE for a full join. In dplyr, use left_join(x, y, by = join_by(id)), inner_join, full_join or anti_join. To stack data frames with the same columns, use rbind() or dplyr::bind_rows().
Part of our guide: How to merge data in pandas, R and SQL
Example data
customers <- data.frame(
customer_id = c("C-101", "C-102", "C-103"),
name = c("Bakery Lune", "Hart & Co", "Nordic Supply")
)
orders <- data.frame(
order_id = c("O-9001", "O-9002", "O-9003", "O-9004"),
customer_id = c("C-101", "C-103", "C-101", "C-104"),
amount = c(120, 75, 80, 60)
)Base R: merge()
| Join | Code |
|---|---|
| Inner | merge(orders, customers, by = "customer_id") |
| Left | merge(orders, customers, by = "customer_id", all.x = TRUE) |
| Right | merge(orders, customers, by = "customer_id", all.y = TRUE) |
| Full | merge(orders, customers, by = "customer_id", all = TRUE) |
| Different key names | merge(x, y, by.x = "cust", by.y = "customer_id") |
merge() sorts the result by the key columns by default; pass sort = FALSE to keep the original order.
dplyr joins
library(dplyr)
orders |> left_join(customers, by = join_by(customer_id))
customers |> anti_join(orders, by = join_by(customer_id)) # never ordered
orders |> inner_join(customers, by = join_by(customer_id),
relationship = "many-to-one")join_by() (dplyr 1.1+) also handles different names (join_by(cust == customer_id)) and inequality joins. relationship = "many-to-one" raises an error if customers has duplicate keys.
| order_id | customer_id | amount | name |
|---|---|---|---|
| O-9001 | C-101 | 120 | Bakery Lune |
| O-9002 | C-103 | 75 | Nordic Supply |
| O-9003 | C-101 | 80 | Bakery Lune |
| O-9004 | C-104 | 60 | NA |
Stacking data frames
rbind(jan, feb)— columns must have the same names (order can differ).dplyr::bind_rows(jan, feb, .id = "source")— fills missing columns with NA and adds a source column.- Many files:
bind_rows(lapply(list.files("sales", full.names = TRUE), read.csv)).
The same joins in pandas and SQL are mapped side by side in merging data in pandas, R and SQL.
Common problems
| Symptom | Cause | Fix |
|---|---|---|
| More rows than expected | Duplicate keys in the lookup table | Check with `anyDuplicated(customers$customer_id)`; set relationship in dplyr |
| Columns named name.x and name.y | Both tables have a non-key column with the same name | Rename first, or set suffixes = c("_order", "_cust") |
| No matches | Key is a factor in one table and character in the other, or has spaces | Convert with as.character() and trimws() |
| Row order changed | merge() sorts by key by default | Use sort = FALSE or dplyr joins |
Frequently asked questions
- How do I do a left join in R?
- Use merge(x, y, by = "key", all.x = TRUE) in base R, or left_join(x, y, by = join_by(key)) in dplyr.
- How do I merge more than two data frames in R?
- Chain joins with the pipe, or use Reduce(function(a, b) merge(a, b, by = "key"), list(df1, df2, df3)).
- What's the difference between merge and rbind?
- merge matches rows on key columns; rbind stacks rows of data frames with the same columns.