Skip to content
Join our email list to get $80 off Ally in November →
R for the Rest of Us Logo

Going Deeper with R

Tidy Data Rule #1: Every Column is a Variable

Transcript

Click on the transcript to go to that point in the video. Please note that transcripts are auto generated and may contain minor inaccuracies.

View code shown in video
# Load Packages -----------------------------------------------------------

library(tidyverse)
library(fs)
library(readxl)
library(janitor)

# Create Directories ------------------------------------------------------

dir_create("data-raw")

# Download Data -----------------------------------------------------------

# https://www.oregon.gov/ode/educator-resources/assessment/Pages/Assessment-Group-Reports.aspx

# download.file("https://www.oregon.gov/ode/educator-resources/assessment/Documents/TestResults2122/pagr_schools_math_tot_raceethnicity_2122.xlsx",
#               mode = "wb",
#               destfile = "data-raw/pagr_schools_math_tot_raceethnicity_2122.xlsx")
#
# download.file("https://www.oregon.gov/ode/educator-resources/assessment/Documents/TestResults2122/TestResults2019/pagr_schools_math_tot_raceethnicity_1819.xlsx",
#               mode = "wb",
#               destfile = "data-raw/pagr_schools_math_tot_raceethnicity_1819.xlsx")
#
# download.file("https://www.oregon.gov/ode/educator-resources/assessment/TestResults2018/pagr_schools_math_raceethnicity_1718.xlsx",
#               mode = "wb",
#               destfile = "data-raw/pagr_schools_math_raceethnicity_1718.xlsx")
#
# download.file("https://www.oregon.gov/ode/educator-resources/assessment/TestResults2017/pagr_schools_math_raceethnicity_1617.xlsx",
#               mode = "wb",
#               destfile = "data-raw/pagr_schools_math_raceethnicity_1617.xlsx")
#
# download.file("https://www.oregon.gov/ode/educator-resources/assessment/TestResults2016/pagr_schools_math_raceethnicity_1516.xlsx",
#               mode = "wb",
#               destfile = "data-raw/pagr_schools_math_raceethnicity_1516.xlsx")

# Import Data -------------------------------------------------------------

math_scores_2021_2022 <-
  read_excel(path = "data-raw/pagr_schools_math_tot_raceethnicity_2122.xlsx") |>
  clean_names()

# Tidy and Clean Data -----------------------------------------------------

third_grade_math_proficiency_2021_2022 <-
  math_scores_2021_2022 |>
  filter(student_group == "Total Population (All Students)") |>
  filter(grade_level == "Grade 3") |>
  select(school_id, contains("number_level")) |>
  pivot_longer(
    cols = -school_id,
    names_to = "proficiency_level",
    values_to = "number_of_students"
  )

Your Turn

Do the following to create a new data frame called enrollment_by_race_ethnicity_2022_2023:

  1. Start with the enrollment_2022_2023 data frame.

  2. select() the district_institution_id and school_institution_id variables as well as those about number of students by race/ethnicity and get rid of all others.

  3. Use pivot_longer() to convert all of the race/ethnicity variables into one variable.

  4. Within pivot_longer(), use the names_to argument to call that variable race_ethnicity.

  5. Within pivot_longer(), use the values_to argument to call that variable number_of_students.

Start with the code below.

# Load Packages -----------------------------------------------------------

library(tidyverse)
library(fs)
library(readxl)
library(janitor)

# Create Directories ------------------------------------------------------

dir_create("data-raw")

# Download Data -----------------------------------------------------------

# https://www.oregon.gov/ode/reports-and-data/students/Pages/Student-Enrollment-Reports.aspx

# download.file("https://www.oregon.gov/ode/reports-and-data/students/Documents/fallmembershipreport_20222023.xlsx",
#               mode = "wb",
#               destfile = "data-raw/fallmembershipreport_20222023.xlsx")
# 
# download.file("https://www.oregon.gov/ode/reports-and-data/students/Documents/fallmembershipreport_20212022.xlsx",
#               mode = "wb",
#               destfile = "data-raw/fallmembershipreport_20212022.xlsx")
# 
# download.file("https://www.oregon.gov/ode/reports-and-data/students/Documents/fallmembershipreport_20202021.xlsx",
#               mode = "wb",
#               destfile = "data-raw/fallmembershipreport_20202021.xlsx")
# 
# download.file("https://www.oregon.gov/ode/reports-and-data/students/Documents/fallmembershipreport_20192020.xlsx",
#               mode = "wb",
#               destfile = "data-raw/fallmembershipreport_20192020.xlsx")
# 
# download.file("https://www.oregon.gov/ode/reports-and-data/students/Documents/fallmembershipreport_20182019.xlsx",
#               mode = "wb",
#               destfile = "data-raw/fallmembershipreport_20182019.xlsx")

# Import Data -------------------------------------------------------------

enrollment_2022_2023 <- read_excel(path = "data-raw/fallmembershipreport_20222023.xlsx",
                                   sheet = "School 2022-23") |> 
  clean_names()

Learn More

There is also a pivot_wider() function that does the opposite of pivot_longer(). You'll learn about this in the Tables lesson.

R for the Rest of Us consultant Albert Rapp has written a couple blog posts on basic and advanced pivoting.

Below, I've embedded a website made by Hasse Walum that provides a visual representation of how the pivot_longer() function works.

There’s also a nice article by Gavin Simpson of University College, London about pivoting. That article includes the animations below, made by Garrick Aden-Buie and Mara Averick, that gave a visual demonstration of pivoting.

Posit has a nice primer on reshaping data, complex with a few exercises.

Finally, a heads up: if you ever see references to the functions gather() and spread(), these are the previous iterations of the pivot functions. They still work (as the tweet below from tidyverse developer Hadley Wickham indicates), but the pivot functions are, in my view (and the view of many others), much easier to use.

Have any questions? Put them below and we will help you out!

You need to be signed-in to comment on this post. Login.

Lilly Yu

Lilly Yu • October 29, 2026

I am still a little confused as to why we'd want to pivot longer in the first place, both in the German speakers data and the proficient students data. For instance, if we wanted to calculate percent change in the number of German speakers between the years, why would the original format not work?

Gracielle Higino

Gracielle Higino Coach • October 31, 2026

Hi Lilly! In R there is always a way to do something, but it doesn't mean they are all following the best standards. It's just that the language is flexible enough to let you choose which approach you want or need to take. There might be cases where reshaping your dataset will not be an option, so it's good to have a function that lets you, for example, calculate the percent change in the number of German speakers between the years even if you don't have your data in a tidy format (in this case you could use the across() function).

However, conceptually, the format of this dataframe is not compliant with the best practices, as each column contains two variables: the year in the column name and the number of people in the cell values. If you want to plot these numbers as a time series, you won't be able to because the variable year is in the column name, not in the data. Another example is if you need to get the range of the time series, or the first and the last year. By pivoting longer and separating the year value, you get all the data you need to calculate all of that and plot a time series!

The bottom line is: the tidier your data, the more you can do with it. It's not that things won't work if it's not tidy, it's that some things might not work or be more complex to implement.

Lilly Yu

Lilly Yu • November 4, 2026

Thank you for this helpful explanation! I actually just yesterday was cleaning and merging a few years of data from one survey, to then create a dataframe to pivot select columns to calculate a ratio between two variables, and realized why the longer format made sense!

Gracielle Higino

Gracielle Higino Coach • November 4, 2026

Nothing like working with our own messy datasets! 😅

Course Content

44 Lessons

New from Ally Translate between base R and the tidyverse