tidyr::pivot_longer() / tidyr::pivot_wider()

pivot_longer(data, cols, names_to = "name", values_to = "value")
Returns: tibble · Updated March 13, 2026 · Tidyverse
tidyr reshape tidyverse pivot data-wrangling

pivot_longer() and pivot_wider() are tidyr functions for reshaping data between long and wide formats. They replace the older gather() and spread() functions with a more intuitive interface.

Syntax

pivot_longer(
  data,
  cols,
  names_to = "name",
  names_prefix = NULL,
  names_sep = NULL,
  names_pattern = NULL,
  values_to = "value",
  values_drop_na = FALSE,
  ...
)

pivot_wider(
  data,
  names_from,
  values_from,
  id_cols = NULL,
  names_prefix = "",
  names_sep = "_",
  values_fill = NULL,
  values_drop_na = FALSE
)

Parameters

pivot_longer()

ParameterTypeDefaultDescription
datatibble / data.frameInput data frame
colscolumnsColumns to pivot into longer format (use tidyselect)
names_tostring”name”Name of the column to store column names
values_tostring”value”Name of the column to store values
names_prefixstringNULLRemove prefix from column names
values_drop_nalogicalFALSEDrop rows where values are NA

pivot_wider()

ParameterTypeDefaultDescription
datatibble / data.frameInput data frame
names_fromcolumnColumn containing names for new columns
values_fromcolumnColumn containing values for new columns
values_fillvalueNULLValue to use for missing combinations
values_drop_nalogicalFALSEDrop rows where values are NA

Examples

Basic pivot_longer

Convert wide data to long format:

library(tidyr)
library(dplyr)

df <- data.frame(
  id = 1:3,
  score_2022 = c(85, 92, 78),
  score_2023 = c(88, 95, 82),
  score_2024 = c(90, 98, 85)
)

df |> pivot_longer(
  cols = starts_with("score_"),
  names_to = "year",
  names_prefix = "score_",
  values_to = "score"
)

# # A tibble: 9 × 3
#      id year  score
#   <dbl> <chr> <dbl>
# 1     1 2022     85
# 2     1 2023     88
# 3     1 2024     90
# 4     2 2022     92
# 5     2 2023     95
# 6     2 2024     98
# 7     3 2022     78
# 8     3 2023     82
# 9     3 2024     85

Basic pivot_wider

Convert long data to wide format:

df_long <- data.frame(
  id = c(1, 1, 2, 2),
  year = c("2022", "2023", "2022", "2023"),
  score = c(85, 88, 92, 95)
)

df_long |> pivot_wider(
  names_from = year,
  values_from = score
)

# # A tibble: 2 × 3
#      id X2022 X2023
#   <dbl> <dbl> <dbl>
# 1     1    85    88
# 2     2    92    95

Multiple value columns

Pivot multiple value columns simultaneously:

df <- data.frame(
  id = 1:2,
  treatment_a = c(10, 15),
  treatment_b = c(20, 25),
  control_a = c(8, 12),
  control_b = c(18, 22)
)

df |> pivot_longer(
  cols = -id,
  names_to = c("group", "outcome"),
  names_sep = "_"
)

# # A tibble: 8 × 4
#      id group    outcome
#   <dbl> <chr>      <dbl>
# 1     1 treatment        10
# 2     1 treatment        20
# 3     1 control           8
# 4     1 control          18
# 5     2 treatment        15
# 6     2 treatment        25
# 7     2 control          12
# 8     2 control          22

Common Patterns

Handling missing values with values_fill

Fill missing values when pivoting wider:

df <- data.frame(
  id = c(1, 1, 2),
  metric = c("a", "b", "a"),
  value = c(10, 20, 30)
)

df |> pivot_wider(
  names_from = metric,
  values_from = value,
  values_fill = 0
)

# # A tibble: 2 × 3
#      id     a     b
#   <dbl> <dbl> <dbl>
# 1     1    10    20
# 2     2    30     0

Using names_pattern for complex column names

Extract multiple pieces information from column names:

df <- data.frame(
  id = 1:2,
  result_q1_pre = c(85, 90),
  result_q1_post = c(92, 95),
  result_q2_pre = c(78, 82),
  result_q2_post = c(88, 90)
)

df |> pivot_longer(
  cols = -id,
  names_to = c("question", "time"),
  names_pattern = "result_(q\\d+)_(pre|post)"
)

See Also