Module 05: Summaries and Decisions

Every summary hides a decision. Make it on purpose.

Chris Chizinski

Slide Accessibility Note

  • Images: none; diagrams are HTML text with labels.
  • Speaker Notes: These slides follow the Module 05 follow-along script. They name the purpose, vocabulary, challenges, and checklist; the live coding happens in class/week_05_follow_along.R.

Today’s Workflow

Clean data
Decide which rows count
Decide categories
group_by + summarize
Check against a known total

A total you cannot check is a guess.

Before We Start

  • Open this module’s repo (accepted in Classroom 50 before class).
  • Open week_05.Rproj, then class/week_05_follow_along.R.
  • Run here::here(): it should print your repo folder.
  • Backup: the follow-along is also on this slide site.

Download the Module 05 follow-along script (.R)

Recap: Module 04

Answer in your script first:

  1. After read_csv(), what do you run first on real data, and why?
  2. A filter dropped 39 of 109 rows. What must you report with the answer?
  3. What does 1987 %/% 10 * 10 give?

Learning Goals

By the end of today you can:

  • summarize a table, and one row per group, with group_by() + summarize()
  • use count() as the shortcut, including two-column counts
  • handle NA in summaries: na.rm = TRUE, and report how many were missing
  • make categories with if_else() (two) and case_when() (many)
  • spot total rows mixed with detail rows, and summarize without double counting
  • explain how the rows and the denominator change a percentage

The Data: Great Lakes Commercial Catch

  • Great Lakes Fishery Commission commercial fish production
  • 65,706 rows x 7 columns, 1867-2015, six lakes
  • values = catch in thousands of pounds
  • Used exactly as released. Via TidyTuesday (2021-06-08)

Vocabulary

Term Meaning
summary one number that stands for many rows: a count, total, mean
grouping group_by() splits rows; summarize() returns one row per group
na.rm = TRUE drop NA before computing: a decision to report, not a fix
total row a row that already sums other rows; include it and you double count
denominator what a percentage is “out of”; change it, change the answer

Cycle 1: One Row Per Group

fishing <- read_csv(here("class", "data", "fishing.csv")) |>
  clean_names()

fishing |> count(lake)                        # you know this one
fishing |> summarize(total = sum(values))     # NA. Why?

fishing |>
  group_by(lake) |>
  summarize(
    rows      = n(),
    n_missing = sum(is.na(values)),           # report what you dropped
    total     = sum(values, na.rm = TRUE)
  ) |>
  arrange(desc(total))

Why Is The Sum NA?

  • 21,916 of 65,706 values are NA (a third of the file)
  • One unknown value makes the total unknown; R refuses to guess
  • na.rm = TRUE drops them. That is your decision
  • So report n_missing right beside the total, every time

sum(is.na(values)): is.na() gives TRUE/FALSE, sum() counts the TRUEs.

A Number That Should Worry You

fishing |>
  filter(lake == "Michigan", species == "Lake Whitefish", year == 2000) |>
  summarize(total = sum(values, na.rm = TRUE))
#> 14381

Is that the U.S. commercial lake whitefish catch from Lake Michigan in 2000?

Challenge 1: Catch Audit (15 min)

Question: where did 14,381 come from?

  • Base: show the rows behind the total (region, values). Compare with the U.S. Total row. How many times too big? Explain with distinct(lake, region)
  • Extension: for every lake, all-rows sum vs U.S. total sum. Which lake is furthest off, and why is that ratio not only double counting?

The Same Catch, Counted Three Times

region values what it is
Green Bay (MI) 1,158 area
Mich. Proper (MI) 1,871 area
MI State Total 3,029 sum of the two above
Green Bay (WI) 116 area
Mich. Proper (WI) 1,648 area
WI State Total 1,765 sum of the two above
Illinois (IL), Indiana (IN) NA not reported
U.S. Total 4,794 sum of the state totals

Lake Michigan lake whitefish, 2000 (thousand lb). All rows sum to 14,381.

Part 2 (Mon Oct 12): Where We Are

  • Part 1: one row per group, and a total that counted the catch three times
  • Today: decide categories, get the right totals, then start the assignment
  • New file today: class/week_05_part2_follow_along.R
  • Assignment 05 is now due Sun Oct 18

Get Part 2: Your First Pull

Pull = copy new changes from GitHub down to your computer (push goes up). I added one new file to your repo on GitHub. Your Part 1 file is not touched.

GitHub Desktop

  1. Commit anything changed
  2. Fetch origin
  3. Pull origin

Positron

  1. Source Control: commit anything changed
  2. … menu > Pull

Check: week_05_part2_follow_along.R is in class/.

Stuck On The Pull?

You see Do this
Desktop: Pull origin, then Push origin Click Pull origin, then Push origin
Positron: “reconcile divergent branches” Terminal: git pull --no-rebase --no-edit, then push
No Pull option / “not a git repository” Open the Module 05 repo folder
Anything else after 3 minutes Download below into class/; we fix git at the break

Download the Part 2 follow-along script (.R)

Checkpoint: Same Start For Everyone

  1. Session > Restart R
  2. Open class/week_05_part2_follow_along.R
  3. Run Section 0, the checkpoint block
  4. nrow(fishing) should print 65706

How each step works today:

  • GOAL and WHY first, then a worked example on tiny data
  • Your turn on the real data, then a CHECK number: match it, move on
  • Challenges: Step 1 for everyone, Step 2 when Step 1 matches

Cycle 2: Categories You Decide

GOAL: label what kind of row each one is. WHY: summing every row counted the same fish three times.

fishing |>
  mutate(recorded = if_else(is.na(values), "missing", "recorded")) |>
  count(lake, recorded)

fishing <- fishing |>
  mutate(
    region_level = case_when(
      region %in% us_totals     ~ "US total",
      region %in% canada_totals ~ "Canada total",
      region %in% state_totals  ~ "state total",
      .default = "area"
    )
  )

fishing |> count(region_level, region)   # did every region land right?

How case_when() Reads

  • Each line: if this ~ then that
  • Lines are checked top to bottom; the first TRUE wins
  • .default catches every row no line matched (forget it: NA)
  • Always count() the result to check it
era = case_when(
  year < 2000 ~ "1950-1999",   # first: now 1867 lands here too
  year < 1900 ~ "1867-1899",   # never reached
  ...
)

Now The Totals Are Right

fishing |>
  filter(region_level == "US total") |>
  group_by(lake) |>
  summarize(n_missing = sum(is.na(values)),
            total = sum(values, na.rm = TRUE),
            .groups = "drop") |>
  arrange(desc(total))
  • Michigan first: 3,072,965 thousand lb
  • Saint Clair last, but 845 of its 1,171 rows are missing. Would you rank it with confidence?

Challenge 2: Species List (20 min)

Question: how many different fish are really in this file?

  • Step 1 (everyone): fix the four misspellings listed in your script with case_when(), keep the rest with .default = species. CHECK: 47 labels
  • Step 2 (Step 1 matches): find the other same-fish labels yourself. How many lakes report each species in the 2000s? Are "Drum" and "Freshwater Drum" the same fish?

Challenge 2: Hint 1, The Plan

Step 1

  1. count(species); find the four labels
  2. mutate() + case_when(): wrong label ~ right label
  3. Keep the rest: .default
  4. count() again; compare with 47

Step 2

  1. Read the 47 labels A to Z: capital letters? plural s?
  2. One line per fix; several labels into one at once
  3. Lakes: 2000s, U.S. totals, known values; count different lakes per species

Still stuck? Hint 2 is in the book: Summaries and Decisions, In-Class Challenge Hints (book chapter 8).

Break (10 min)

Integrated Challenge (30 min)

Lake whitefish, Lake Michigan, U.S. catch: total catch by decade, with the number of years and missing years beside each total. Which decade peaked?

  • Step 1 (everyone): fill in the pipeline in your script. Answer in pounds, not thousands. CHECK: 1990s, 69,967
  • Step 2 (Step 1 matches): the 2010s look low. Compute catch per recorded year. Which decade “wins” now, and should you trust it?

Integrated Challenge: Hint 1, The Plan

Step 1

  1. Keep Michigan, lake whitefish, U.S. total rows
  2. decade from year (1987 becomes 1980)
  3. One row per decade
  4. Years, number missing, total catch
  5. Biggest catch first

Step 2

  1. How many years does each decade have? Data end in 2015
  2. New column: catch / recorded years
  3. Sort by decade; read it top to bottom
  4. The new “winner”: how many years is it built on?

Still stuck? Hint 2 is in the book: Summaries and Decisions, In-Class Challenge Hints (book chapter 8).

Every Fix Raises The Next Question

  • By total, the 1990s peak: 69,967 thousand lb (about 70 million lb)
  • The 2010s look halved, but the data end in 2015: 6 years, not 10
  • Per year, the 2010s dip only a little (4,265 vs 4,575)
  • Per year, the 1870s “win”, on one recorded year

Report a rate with the number of years behind it.

Assignment 05: Park Species Decisions

  • Data: NPS species lists, 15 most-visited national parks (61k records)
  • Decide: which records count as “in the park”; one category for two kinds of “unknown”
  • Summarize: percent non-native per park. Watch the denominator
  • Investigate: dplyr::across() to total several columns at once
  • Due Sun Oct 18, 11:59 PM. Last push before then is graded

How It Is Graded (10 pts)

Criterion Points
Renders + .html pushed 2
Cleaning and types 2
Core tasks 3
Investigate 1
Written answers 1
README 1
  • Only assignment/ and README.md are checked. class/ never is.
  • A red X is feedback, not a grade.

Assignment Workshop (35 min)

  • Open assignment/assignment.qmd. Goal today: Tasks 1 and 2
  • Task 2, decide which records count, is the same move as region_level today
  • Run it chunk by chunk. The full render fails until later tasks have code: expected, not a problem
  • Done with 1 and 2? Keep going
  • Last 3 minutes: commit and push assignment/

Before You Leave