R tidyr Reshape Data

tidyr reshapes data between wide format (many columns) and long format (many rows). Most visualization and statistical functions in R prefer long format. Most human-readable data arrives in wide format. tidyr's two main functions — pivot_longer() and pivot_wider() — convert between these formats.

Wide vs Long Format

WIDE FORMAT (human-friendly):
  Student  Math  Science  English
  Asha     85    90       78
  Balu     72    80       88
  Cena     91    85       92

LONG FORMAT (R-friendly for analysis):
  Student  Subject   Score
  Asha     Math      85
  Asha     Science   90
  Asha     English   78
  Balu     Math      72
  Balu     Science   80
  Balu     English   88
  Cena     Math      91
  ...

pivot_longer() — Wide to Long

library(tidyr)
library(dplyr)

wide <- data.frame(
  student = c("Asha","Balu","Cena"),
  Math    = c(85, 72, 91),
  Science = c(90, 80, 85),
  English = c(78, 88, 92)
)

long <- pivot_longer(
  wide,
  cols      = c(Math, Science, English),  # columns to collapse
  names_to  = "subject",                  # new column for old column names
  values_to = "score"                     # new column for values
)

print(long)
#   student subject score
# 1    Asha    Math    85
# 2    Asha Science    90
# 3    Asha English    78
# 4    Balu    Math    72
# ...

pivot_longer() with cols Helpers

# Select columns to pivot using helpers
pivot_longer(wide, cols=-student, names_to="subject", values_to="score")
pivot_longer(wide, cols=starts_with("M"), names_to="subject", values_to="score")
pivot_longer(wide, cols=2:4, names_to="subject", values_to="score")

pivot_wider() — Long to Wide

# Reverse: go from long back to wide
wide_again <- pivot_wider(
  long,
  names_from  = "subject",   # column whose values become column names
  values_from = "score"      # column whose values fill the table
)

print(wide_again)
# student  Math Science English
# Asha       85      90      78
# Balu       72      80      88
# Cena       91      85      92

separate() — Split One Column Into Two

df <- data.frame(
  full_name = c("Asha Sharma", "Balu Kumar", "Cena Patel"),
  dob       = c("1995-08-15", "1992-03-22", "1998-11-05")
)

df |>
  separate(full_name, into=c("first","last"), sep=" ") |>
  separate(dob, into=c("year","month","day"), sep="-")
#   first   last  year month day
# 1  Asha Sharma  1995    08  15
# 2  Balu  Kumar  1992    03  22
# 3  Cena  Patel  1998    11  05

unite() — Combine Two Columns Into One

df2 <- data.frame(
  first = c("Asha","Balu"),
  last  = c("Sharma","Kumar"),
  year  = c(2024, 2024),
  month = c(8, 9)
)

df2 |>
  unite("full_name", first, last, sep=" ") |>
  unite("period", year, month, sep="-")

fill() — Fill Missing Values Downward

df3 <- data.frame(
  month  = c("Jan", NA, NA, "Feb", NA),
  sales  = c(1000, 1200, 800, 1500, 900)
)

fill(df3, month, .direction="down")
#   month sales
# 1   Jan  1000
# 2   Jan  1200
# 3   Jan   800
# 4   Feb  1500
# 5   Feb   900

drop_na() and replace_na()

df4 <- data.frame(x=c(1,NA,3), y=c("a",NA,"c"))
drop_na(df4)            # remove rows with any NA
replace_na(df4, list(x=0, y="unknown"))   # fill NAs

Data reshaping is often the most time-consuming part of a data project. pivot_longer() and pivot_wider() handle 90% of reshape tasks. separate() and unite() handle the column splitting and joining that clean messy composite fields. Together, these tools get your data into whatever shape a function or visualization needs.

Leave a Comment

Your email address will not be published. Required fields are marked *