tidyr nesting() in R: Preserve Column Pairs in Combinations
The nesting() function in tidyr preserves existing column pairings inside expand() or complete(), instead of generating their full cross product.
df |> expand(nesting(year, quarter), product) df |> complete(nesting(user, plan), month) df |> expand(year, quarter) # without nesting: full cross product expand(year = 2020:2024, quarter = 1:4) # all 5 * 4 = 20 combos
Need explanation? Read on for examples and pitfalls.
What nesting() does in one sentence
nesting(...) is a tidyselect helper used inside expand() or complete() that keeps the named columns' EXISTING pairings together (treats them as one composite column) instead of cross-producting them.
Syntax
nesting(...). Used inside expand() or complete(). Not called standalone.
nesting() when some column combinations should not be cross-producted. E.g., year-quarter where (2025, q2) might not yet exist; you want to preserve actual time periods only.Five common patterns
1. Year-quarter as a unit
2. Hierarchical: parent-child
3. complete() with nesting
4. Multi-level nesting
5. Without nesting comparison
nesting() is the cure for "I don't want a Cartesian product of columns A and B". When A and B have a natural pairing (like year-quarter, country-state), nesting keeps that intact while cross-producting them with OTHER columns.nesting() vs cross-product (default)
| Behavior | Without nesting | With nesting |
|---|---|---|
| Year x Quarter | All combos | Only existing pairs |
| Output rows | n_year * n_quarter | n_unique_pairs |
| Generates impossible rows | Yes | No |
When to use nesting:
- Hierarchical structures (country-state, year-quarter, customer-plan).
- Time-stamped pairs that mustn't be artificially split.
- When you only want combinations that EXIST in the data.
A practical workflow
Use nesting in time-series with multiple subjects to avoid generating cross-subject combinations.
Each (patient, treatment) pair gets visits 1-10; doesn't cross-product patient with all treatments.
Common pitfalls
Pitfall 1: confusing nesting with cross-product. Default behavior in expand/complete is FULL cross-product. nesting prevents that for the named columns.
Pitfall 2: trying to use nesting outside expand/complete. It is a tidyselect helper, not a standalone function.
nesting() only preserves pairs that EXIST in the data. It doesn't generate any new pairs, only cross-products with the OTHER expand/complete arguments.Try it yourself
Try it: Generate all (cyl, gear) x am combinations using nesting to preserve cyl-gear pairs from mtcars. Save to ex_nested.
Click to reveal solution
Explanation: Without nesting, you'd get 332 = 18 rows (including nonexistent (4, 5) etc). With nesting, only the actual cyl-gear pairs.
Related tidyr functions
After mastering nesting, look at:
expand(): combinations from existing datacomplete(): expand + merge with originalexpand_grid()/crossing(): from vectorstidyr::nest()/unnest(): list-column workflowsdplyr::group_by(): alternative for some uses
FAQ
What does nesting do in tidyr?
nesting(...) is a helper used inside expand/complete that preserves existing column pairings. Instead of cross-producting the named columns, it keeps them as observed pairs.
When should I use nesting?
When two or more columns have a natural pairing (year-quarter, country-state) and cross-producting them would create impossible rows.
Does nesting work outside expand/complete?
No. It is a tidyselect helper specifically for those functions.
What is the difference between nesting and group_by?
group_by changes how subsequent verbs operate (per-group). nesting controls combination generation in expand/complete only. Different scopes.
Can I use nesting with three or more columns?
Yes. nesting(country, state, city) preserves all three together. Use any number of columns.