tidyr chop() in R: Group Rows Into List Columns

The chop() function in tidyr collapses rows within groups into list-column cells. Each cell holds a vector of the values from rows in that group. It is a lighter-weight version of nest().

⚡ Quick Answer
df |> chop(c(value))                    # vector list-col, one per group row
df |> chop(c(value, score))              # multiple list cols
df |> unchop(value)                       # opposite
df |> nest(.by = group)                   # different: tibble list col

Need explanation? Read on for examples and pitfalls.

📊 Is chop() the right tool?
STARTcollapse rows into vector list-colchop()collapse rows into tibble list-colnest()expand list col back to rowsunchop()specific cols onlychop(c(specific_cols))

What chop() does in one sentence

chop(data, cols) collapses each group's rows in cols into a list-column where each cell is a vector of the original values. Lighter than nest, which creates a tibble per cell.

Syntax

chop(data, cols, ..., error_call = caller_env()). cols are the columns to collapse.

Run live
Run live, no install needed. Every R block on this page runs in your browser. Click Run, edit the code, re-run instantly. No setup.
RChop values per group
library(tidyr) library(dplyr) library(purrr) df <- tibble( user = c("a","a","b","b","b"), visits = c(1, 2, 3, 4, 5) ) df |> chop(visits) #> # A tibble: 2 x 2 #> user visits #> <chr> <list> #> 1 a <int [2]> #> 2 b <int [3]>

  
Tip
chop is faster than nest when you only need vector list-cols, not tibbles. Use chop for "collapse this column into per-group lists".

Five common patterns

1. Standard chop

ROne vector per group
df |> chop(visits)

  

Group columns are detected automatically (the unchosen ones).

2. Multiple chopped columns

RTwo list-cols, parallel
df |> chop(c(visits, score))

  

3. Compute on chopped lists

RPer-row aggregation
df |> chop(visits) |> mutate(total = purrr::map_dbl(visits, sum), n = purrr::map_int(visits, length))

  

4. Round-trip with unchop

Rchop then unchop
df |> chop(visits) |> unchop(visits) |> identical(df)

  

5. Compare with nest

Rchop is lighter
df |> chop(visits) # list of integer vectors df |> nest(data = visits) # list of 1-column tibbles (heavier)

  
Key Insight
chop is to nest as vectors are to data frames. chop creates list-of-vector cells; nest creates list-of-tibble cells. For single-column collapsing, chop is more efficient.

chop() vs nest() vs summarise(list(...))

Function Cell type Best for
chop(col) Vector Single-column collapse
nest(.by = g) Tibble Multi-column collapse
summarise(x = list(col)) Vector (manual) Inside summarise pipelines

A practical workflow

Use chop for "list of values per group" patterns.

RInteractive R
events |> chop(timestamp) |> mutate(first_event = purrr::map_int(timestamp, ~ as.integer(min(.x))), last_event = purrr::map_int(timestamp, ~ as.integer(max(.x))))

  

Per group: list of timestamps, plus first and last.

Common pitfalls

Pitfall 1: forgetting cols argument. chop(df) with no cols just returns df. You must specify which columns to collapse.

Pitfall 2: confusing with nest. chop = vectors; nest = tibbles. Different cell types.

Warning
chop() collapses by ALL non-chopped columns implicitly. All other columns must be unique per group, or chop creates surprising groupings.

Try it yourself

Try it: Chop the value column per group, count items per group. Save to ex_chopped.

RYour turn: chop and count
df <- tibble(g = c("a","a","b"), v = c(1, 2, 3)) ex_chopped <- df |> # your code here ex_chopped$count #> Expected: c(2, 1)

  
Click to reveal solution
RSolution
ex_chopped <- df |> chop(v) |> mutate(count = purrr::map_int(v, length)) ex_chopped$count #> [1] 2 1

  

Explanation: chop(v) collapses values per g; map_int(v, length) counts items.

After mastering chop, look at:

  • unchop(): opposite (vectors to rows)
  • nest(): tibble version
  • unnest(): tibble version of unchop
  • summarise(list(...)): alternative for collapse

FAQ

What does chop do in tidyr?

chop(data, cols) collapses each group's rows in cols into a list-column where each cell is a vector. Lighter than nest.

What is the difference between chop and nest?

chop creates VECTOR list cells. nest creates TIBBLE list cells. chop for single-column collapse; nest for multi-column.

Is chop faster than nest?

Slightly, for single-column cases. The difference is small but chop avoids the tibble wrapping.

How do I expand chopped columns?

unchop(col) is the inverse. Returns the original row-per-row form.

When should I use chop vs nest?

chop for one or a few related columns into vectors. nest for collapsing all non-grouping columns into a tibble.