Tidy Data Rule #1: Every Column is a Variable
This lesson is called Tidy Data Rule #1: Every Column is a Variable, part of the R in 3 Months (Spring 2026) course. This lesson is called Tidy Data Rule #1: Every Column is a Variable, part of the R in 3 Months (Spring 2026) course.
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.
Loading transcript...
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:
Start with the
enrollment_2022_2023data frame.select()thedistrict_institution_idandschool_institution_idvariables as well as those about number of students by race/ethnicity and get rid of all others.Use
pivot_longer()to convert all of the race/ethnicity variables into one variable.Within
pivot_longer(), use thenames_toargument to call that variablerace_ethnicity.Within
pivot_longer(), use thevalues_toargument to call that variablenumber_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!
Course Content
148 Lessons
You need to be signed-in to comment on this post. Login.
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 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 • 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 Coach • November 4, 2026
Nothing like working with our own messy datasets! 😅