Activity: Wrangling Your Data
Importing more than one way, reshaping columns, and saving what you make
Worksheet: Wrangling Your Data
How to use this worksheet
Work through each part in order, at your own pace. Type every line of code yourself into a plain R script — do not copy-paste. Blocks marked ▶ Run this are code you should type and execute. Blocks marked ✏️ Your turn ask you to write, modify, or answer something. Boxes marked 🚀 If you finish early are optional bonus material.
Part 1 · Import the same data two ways
▶ Run this in your Script:
library(tidyverse)
library(readxl)
pine_csv_df <- read_csv("data/pine_needles.csv")
pine_excel_df <- read_excel("data/pine_needles.xlsx")▶ Run this:
glimpse(pine_csv_df)
dim(pine_csv_df)✏️ Your turn: List the six column names and, for each, its type (<chr>, <dbl>, etc.):
Column: Type:
Column: Type:
Column: Type:
Column: Type:
Column: Type:
Column: Type:
Part 2 · filter() — picking rows
▶ Run this:
pine_csv_df |> filter(n_s == "n") |> head()
pine_csv_df |> filter(length_mm > 20) |> head()✏️ Your turn: Write a filter() that keeps only sunny (sun == "sunny") needles longer than 15 mm.
# Write your code here:
⚠️ Watch out!
filter(n_s = "n")(one=) is an error. You need==to test equality.
Part 2b · filter() — OR, %in%, and missing values
▶ Run this:
# OR: shady side, OR any exceptionally long needle
pine_csv_df |> filter(n_s == "n" | length_mm > 22)
# %in%: match one of several named teams
pine_csv_df |> filter(group %in% c("cephalopods", "salmon"))✏️ Your turn: Rewrite the %in% line above as a chain of | conditions instead — same result, longer code.
# Write your code here:
▶ Run this:
survey_df <- tibble(
site = c("A", "B", "C", "D"),
count = c(12, NA, 8, NA)
)
survey_df |> filter(is.na(count))
survey_df |> filter(!is.na(count))✏️ Your turn: Write a filter() on pine_csv_df that keeps sunny (sun == "sunny") needles that are also longer than 14mm — think about whether you need ,/& or |.
# Write your code here:
Part 3 · select() — picking columns
▶ Run this:
pine_csv_df |> select(n_s, length_mm) |> head()
pine_csv_df |> select(-date) |> head()✏️ Your turn: Write a select() that keeps every column except tree_no.
# Write your code here:
Part 4 · mutate() — new and changed columns
▶ Run this:
pine_csv_df |>
mutate(length_cm = length_mm / 10) |>
head()
pine_csv_df |>
mutate(log_length = log(length_mm)) |>
head()✏️ Your turn: Add a column called size_class that is "long" when length_mm > 18 and "short" otherwise. Use if_else().
# Write your code here:
✏️ Your turn: Does mutate() change how many rows your data frame has? How many columns did each example above add?
________________________
🚀 If you finish early: Add a second transformed column — a square-root transform (sqrt(length_mm)) alongside your log transform. Which one looks like it changes the spread of the values more?
Part 4b · case_when() — recoding and binning
▶ Run this:
pine_csv_df |>
mutate(
size_class = case_when(
length_mm < 16 ~ "short",
length_mm < 20 ~ "medium",
TRUE ~ "long"
)
) |>
count(size_class)✏️ Your turn: Change the two cutoffs (16 and 20) to different values of your choosing. How do the counts in each bin shift?
# Write your code here:
✏️ Your turn: What happens if you delete the TRUE ~ "long" line and rerun? Try it, then explain why that’s dangerous.
____________________________________________________________________________________
▶ Run this:
pine_csv_df |>
mutate(
team_code = case_when(
group == "cephalopods" ~ "CEPH",
group == "salmon" ~ "SALM",
group == "crayfish" ~ "CRAY",
group == "snail" ~ "SNAI",
TRUE ~ "UNK"
)
) |>
distinct(group, team_code)✏️ Your turn: Add a size_class column (as above) to pine_wrangled_df’s recipe — you’ll build the full pipeline with it in Part 6.
🚀 If you finish early: Write a case_when() that uses two conditions per row (e.g., n_s == "n" & length_mm > 20 ~ "long shady") to build a combined category.
Part 5 · arrange() — sorting rows
▶ Run this:
pine_csv_df |> arrange(length_mm)
pine_csv_df |> arrange(desc(length_mm))✏️ Your turn: Sort the data by n_s, and within each side, by length_mm from longest to shortest.
# Write your code here:
Part 6 · The full pipeline
▶ Run this:
pine_wrangled_df <- pine_csv_df |>
filter(length_mm > 0) |>
select(group, n_s, sun, length_mm) |>
mutate(
length_cm = length_mm / 10,
log_length = log(length_mm),
size_class = case_when(
length_mm < 16 ~ "short",
length_mm < 20 ~ "medium",
TRUE ~ "long"
)
) |>
arrange(n_s, desc(length_mm))
head(pine_wrangled_df)✏️ Your turn: In your own words, read this pipeline out loud, step by step, the way we did in lecture.
Take pine_csv_df, then ______________________, then ______________________,
then ______________________, then ______________________.
Part 7 · Finding duplicate rows
▶ Run this:
needle_check_df <- read_csv("data/pine_needles_error.csv")
nrow(needle_check_df)
n_distinct(needle_check_df)✏️ Your turn: Do nrow() and n_distinct() match?
What does that tell you?
_______________________________________________________________________
▶ Run this:
needle_check_df |> filter(duplicated(needle_check_df))
needle_clean_df <- needle_check_df |> distinct()
nrow(needle_clean_df)✏️ Your turn: How many rows did distinct() remove? Why does this matter for the sample size you’d report?
_________________________________________________________________________
⚠️ Watch out!
distinct()only catches exact duplicate rows — a mistiped name won’t be caught. Always look at your data too.
Part 8 · Saving your work with write_csv()
▶ Run this:
write_csv(pine_wrangled_df, "data/pine_needles_wrangled.csv")Check your data/ folder — the new CSV should be there, alongside (not instead of) the raw pine_needles.csv.
✏️ Your turn: Save your cleaned needle_clean_df from Part 7 to data/pine_needles_error_clean.csv.
# Write your code here:
⚠️ Watch out! Never give a wrangled file the same name as the raw file. If your script’s output can overwrite your raw data, one accidental re-run destroys it permanently.
Part 9 · Review and checkpoint
At this point you can:
✏️ Your turn — before you move on: Run your whole script top to bottom.
Ran cleanly? Y / N — if not, the error was:
____________________________________________________________
📤 What to turn in before next class
Upload both of these to the course management system:
- Your code — the
scripts/folder (or just02_wrangling_data.R) - This worksheet, with your written answers
Getting unstuck
When code breaks — and it will, that is normal:
- Read the error message out loud. R usually names the line and the problem.
- Check the usual suspects: did you run
library(tidyverse)andlibrary(readxl)?- Spelling?
- A missing
)or a|>at the start of a line?
?function_nameopens the built-in help page.- Bring the exact error (copy-paste it) to class or office hours.
💡 Key idea: Every working scientist googles error messages daily. Getting stuck is not failing — it is the job.