dplyr, janitor, tidyr, and broom — cleaning, summarizing, and comparing real data.
JP AdjadehEPPS, UT Dallas
Before You Start
What's the tidyverse?
A collection of R packages built around a shared, verb-based grammar for working with data. Where base R uses bracket indexing — data[row, column] — the tidyverse uses named functions like select(), filter(), and mutate(), chained together in a readable sequence.
Before You Start
The pipe: |>
The native pipe |> takes what's on its left and feeds it as the first argument to what's on its right. life_exp |> filter(...) reads as "take life_exp, then filter it."
You'll also see %>% in older tutorials and Stack Overflow answers — same idea, from the magrittr package rather than base R. We'll use |> today.
Package 1 of 4
dplyr
d
The core grammar of data manipulation. A small set of verbs — select(), filter(), arrange(), mutate(), summarize() — cover most of what indexing and subsetting did in base R.
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
dplyr — Task 2
filter()
Your Task
Using dplyr and the pipe (|>), write R code to filter life_exp to only rows where Gender is "Female."
Check Your Code
Keeping rows that match
life_exp |> filter(Gender == "Female")
dplyr — Task 2, Prompted
Now try the prompt
life_exp |> filter(Gender == "Female")
Now Prompt It
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
dplyr — Task 3
arrange()
Your Task
Using dplyr and the pipe (|>), write R code to arrange life_exp by Average_Life_Expectancy from highest to lowest. Identify which year and gender had the highest value.
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
dplyr — Task 4
group_by() + summarize()
Your Task
Using dplyr and the pipe (|>), write R code to group life_exp by Gender and compute the average Average_Life_Expectancy for each group. This is your first real summary table — print it, and save it to your working directory as a CSV.
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
Package 2 of 4
janitor
j
Practical cleaning utilities dplyr doesn't include. clean_names() converts inconsistent column names into a consistent format automatically. tabyl() gives quick frequency counts.
Point and click
Packages pane → Install → type "janitor" → Install
By code
p_load(janitor)
Now Using
air
janitor — Task 1
clean_names()
Your Task
Using the janitor package, write R code to clean up air's column names so they're consistent. Check names(air) before and after.
Check Your Code
Consistent names, automatically
names(air) air <- air |> clean_names() names(air)
Type_Name becomes type_name, Data.Value becomes data_value — every column now follows the same pattern.
janitor — Task 1, Prompted
Now try the prompt
names(air) air <- air |> clean_names() names(air)
Now Prompt It
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
dplyr on air — Task 5
Chaining filter, group, and summarize
Your Task
Using dplyr and the pipe (|>), write R code to filter air to rows where type_name is "UHF42," then group by name and compute the average data_value for each. Print the result and save it to your working directory as a CSV.
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
janitor — Task 2
tabyl()
Your Task
Using janitor and the pipe (|>), write R code to get a quick count of how many rows exist for each name in air — which one appears most? Print the result and save it to your working directory as a CSV.
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
Debug It
Find the bug
air |> filter(Type_Name == "UHF42") |> nrow()
Console OutputError in `filter()`:
i In argument: `Type_Name == "UHF42"`.
Caused by error:
! object 'Type_Name' not found
Your Task
This should count how many UHF42 rows are in air, but it throws an error. Prompt an AI to diagnose the problem — then verify its explanation and its fix actually work before trusting either.
Check Your Fix
The column name changed earlier
air |> filter(type_name == "UHF42") |> nrow()
clean_names() ran earlier in this session and converted Type_Name to type_name. The error wasn't a dplyr problem — it was a reference to a column name that no longer exists. An AI can spot this instantly, but only checking the actual column names confirms it's right.
Package 3 of 4
tidyr
t
Handles reshaping and missing data. Two functions matter most today: drop_na() removes rows with missing values; replace_na() fills them with a specified value instead.
Point and click
Packages pane → Install → type "tidyr" → Install
By code
p_load(tidyr)
Now Using
ev
Before You Start
Drop it, or fill it in?
AI can write drop_na() or replace_na() code in seconds. It cannot tell you which one is right — that depends on why the value is missing and what your analysis needs. That judgment call is yours.
tidyr — Task 1
drop_na() vs. replace_na()
Your Task
Using tidyr and the pipe (|>), write R code to count how many rows in ev have a missing County value, then try both drop_na(County) and replace_na() with a placeholder, and compare the resulting row counts.
Check Your Code
Two different outcomes
sum(is.na(ev$County))
ev |> drop_na(County) |> nrow() ev |> replace_na(list(County = "Unknown")) |> nrow()
drop_na() shrinks the dataset; replace_na() keeps every row but changes what's in it. Neither is automatically correct.
tidyr — Task 1, Prompted
Now try the prompt
sum(is.na(ev$County))
ev |> drop_na(County) |> nrow() ev |> replace_na(list(County = "Unknown")) |> nrow()
Now Prompt It
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
Capstone Table
Top manufacturers by count
Your Task
Using dplyr and the pipe (|>), write R code to create a Vehicle_Age column in ev, group by Make, compute the count and average Electric_Range for each manufacturer, and show the top 10 by count. Print the result and save it to your working directory as a CSV.
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
One More Thing
Polish the output
knitr::kable() turns any data frame into a clean, presentable table — the same one you'd drop into a report or paper.
top_makes |> knitr::kable()
Package 4 of 4
broom
b
Converts model objects into tidy data frames. tidy() turns a regression's coefficients into rows and columns you can filter, sort, and merge like any other data — instead of a wall of console text.
Point and click
Packages pane → Install → type "broom" → Install
By code
p_load(broom)
Now Using
life_exp
broom — Task 1
Fit three models
Your Task
Using base R's lm(), write R code to fit three linear models on life_exp: Model 1 predicts Average_Life_Expectancy from Year; Model 2 adds Gender; Model 3 adds Age_adjusted_Death_Rate. Look at summary() on each.
Check Your Code
Three nested models
model1 <- lm(Average_Life_Expectancy ~ Year, data = life_exp) model2 <- lm(Average_Life_Expectancy ~ Year + Gender, data = life_exp) model3 <- lm(Average_Life_Expectancy ~ Year + Gender + Age_adjusted_Death_Rate, data = life_exp)
summary() gives you a full report per model, but comparing three of these side by side means reading three separate walls of text.
broom — Task 1, Prompted
Now try the prompt
model1 <- lm(Average_Life_Expectancy ~ Year, data = life_exp) model2 <- lm(Average_Life_Expectancy ~ Year + Gender, data = life_exp) model3 <- lm(Average_Life_Expectancy ~ Year + Gender + Age_adjusted_Death_Rate, data = life_exp)
Now Prompt It
Now that you've seen this work, write an AI prompt that would get you to this same code. Compare what the AI gives you to what you already wrote.
broom — Task 2
Merge the three into one table
Your Task
Using broom's tidy() and dplyr's bind_rows(), write R code to convert each model into a data frame, add a column labeling which model each row came from, and combine all three into one. Print the result and save it to your working directory as a CSV.