Intro to data wrangling and visualization with the tidyverse

Practice the single-table dplyr verbs and the fundamentals of plotting with ggplot2.

Slides

Make slides full screen

Exercise 1: Data wrangling

Mode setup. The room is split into Group A and Group B. For Exercise 1, Group A works agentic — Claude Code reads your notebook, writes code, and runs it. Group B works manual — you write each step yourself; you may ask a chat LLM conceptual questions, but it does not act in your environment. Exercise 2 flips the modes.

Open notebooks/03-wrangling.qmd in your Codespace and work through the parts below.

Part 1 — Predict (no code)

Look at table_02 and answer in the notebook (in plain words, no code yet):

  1. How many rows? What does one row represent?
  2. After table_02 |> filter(time_point == "baseline"), how many rows? What does each row represent now?
  3. After table_02 |> count(arm), how many rows? What does each row represent now?

We’ll briefly compare answers across the room before moving to code.

Part 2 — Build the pipeline

Build one growing pipeline on table_02, one verb per step. Whatever your assigned mode, the goal is the same.

  1. Pipe table_02 into head().
  2. Filter to the baseline time point.
  3. Add select() to keep pid, arm, ph, nugent_score, crp_blood.
  4. Add mutate() to create high_ph = if_else(ph >= 4.5, "yes", "no").
  5. Add arrange(desc(ph)).
  6. Assign the final pipeline to early_visit.
  7. As a separate one-liner, run table_02 |> count(arm) and confirm it matches your Part 1 prediction.

Part 3 — Reach (optional)

Classify Nugent score into clinical categories with case_when():

  • 0–3 → "normal"
  • 4–6 → "intermediate"
  • 7–10 → "BV"

Add the category column to early_visit, then run count(arm, nugent_category) to see the distribution by treatment arm.

Exploring data for patterns

Reproduced from a Schmidt Science Fellowship activity, thanks to Fatima Hussain.

Patterns are the essence of data exploration, and your eye is good at finding them once the data is drawn well. The same numbers can hide or reveal a pattern depending on how you choose to show them. This activity asks you to make those choices by hand, before writing any ggplot code.

The data

Eight Mycobacterium tuberculosis strains (S1–S8) were profiled for five genes involved in drug resistance: rpoB (rifampicin), katG and inhA (isoniazid), embB (ethambutol), and pncA (pyrazinamide).

Table 1 gives the allele each strain carries for each gene, either Wild Type or Mutant. Table 2 gives how strongly each gene is expressed, from 0 (none) to 3 (high).

Table 1: Allele carried by each strain
gene S1 S2 S3 S4 S5 S6 S7 S8
rpoB Wild Type Mutant Wild Type Mutant Wild Type Wild Type Mutant Mutant
katG Wild Type Mutant Wild Type Mutant Wild Type Wild Type Mutant Mutant
inhA Wild Type Wild Type Wild Type Mutant Wild Type Wild Type Mutant Wild Type
embB Wild Type Mutant Wild Type Wild Type Wild Type Wild Type Mutant Mutant
pncA Wild Type Wild Type Mutant Mutant Wild Type Wild Type Wild Type Mutant
Table 2: Expression level for each gene
gene S1 S2 S3 S4 S5 S6 S7 S8
rpoB 2 2 1 2 2 1 2 2
katG 2 1 2 1 2 2 1 1
inhA 1 3 1 3 1 1 3 2
embB 1 2 1 1 2 1 2 2
pncA 1 1 1 1 1 2 1 1

Somewhere in here is a group of drug-resistant strains. Printed as plain tables, it is hard to see.

Your turn

Work in a pair or trio with whoever is next to you.

  1. Open a blank canvas at excalidraw.com. No login needed.
  2. Sketch one visualization of this data that would let someone spot which strains are drug-resistant.
  3. Describe your sketch in the vocabulary from the slides:
    • What is your mark, the thing you draw for each piece of data?
    • What are your mappings: which column controls position, which controls color or fill, which controls shape or size?
    • What geom would draw this in ggplot?
  4. Export the canvas as an image (top-left menu, then “Export image”) and post it, with your three answers, to #module-3-sketches on Discord.

We will put a few sketches on the screen and talk through the choices behind them.

Exercise 2: Data visualization

Mode flip. If you were agentic in Exercise 1, you are manual now; if you were manual, you are agentic. Same definitions as before — manual means you may ask a chat LLM conceptual questions but it does not touch your notebook.

Open notebooks/03-visualization.qmd in your Codespace.

Part 1 — Read two plots (no code)

The notebook shows two pre-built plots: a scatter (ph vs nugent_score, colored by arm) and a boxplot (ph by arm). For each plot, answer in plain words:

  1. What is the mark?
  2. What property of the mark comes from what column?
  3. How many rows of data made each mark?

Then sketch (excalidraw or paper) a plot that would answer “does pH differ between treatment arms?” Name the mark, the mappings, and how many rows make each mark.

Part 2 — Build the plot

Build one ggplot that grows step by step, on the raw table_02. Whatever your assigned mode, the goal is the same.

  1. ggplot(table_02, aes(x = ph, y = nugent_score)) + geom_point(). Confirm each row is one point.
  2. Swap geom_point() for geom_jitter().
  3. Add aes(color = arm).
  4. Switch to a boxplot: aes(x = arm, y = ph) + geom_boxplot(). Notice the row-to-mark ratio changes.
  5. Compare aes(fill = arm) (mapping) with fill = "navyblue" (parameter). Run both.
  6. Add labs(x = "Treatment arm", y = "Vaginal pH", fill = "Arm").
  7. Add facet_wrap(~ time_point).

Part 3 — Reach (optional)

Produce a polished final figure for the question. Facet by visit, color by arm, axes labeled, time_point panels in chronological order (baseline, week_1, week_7), and a clean theme.