Compare patterns

Coding Lesson 3 · Math Camp 2026

API 209

Before we begin

ARRIVAL PROMPT

What makes a graph persuasive, and what makes it dangerous?

Write one feature in each category. Be ready to explain why the same feature might do both.

2 MINUTES

Compare patterns

MON AUG 24 · 1-4 PM

Move from an analysis table to a defensible comparison: plot, model, diagnose, explain.

Today

  1. Define the comparison and analysis sample.
  2. Build a plot progressively.
  3. Use scale and transformation deliberately.
  4. Fit and interpret one simple regression.
  5. Inspect residuals and surprising observations.
  6. Match the written claim to the design.

By 4 PM

  • Map variables to appropriate visual encodings.
  • Explain why a logarithmic scale can change what we see.
  • Identify the observations used in a plot or model.
  • Interpret a coefficient in units.
  • Separate description, association, prediction, and causation.
  • Use an agent for separate code and statistical reviews.

Start with the question

How is national income associated with under-five mortality, and how does the pattern differ across income levels?

The question gives us:

  • a population,
  • an outcome,
  • a comparison variable,
  • possible groups,
  • no causal identification strategy.

Name the analysis sample

health_analysis <- health |>
  filter(analysis_sample)

nrow(health_analysis)
range(health_analysis$year)
n_distinct(health_analysis$iso3c)

A result describes the rows that entered it, not every row in the source.

A graph is an argument

Every graph chooses:

  • what to include,
  • what to omit,
  • how to position values,
  • which differences to emphasize,
  • which comparison becomes easy.

The code is part of that argument.

Every graph is a map

A map is useful because it leaves most of the world out.

A figure or model does the same:

  • preserves features needed for one comparison,
  • suppresses details that do not fit the current view,
  • becomes dangerous when we forget what was omitted.
The question is not “Did we include everything?” It is “Is this the right map for this journey?”

Critique before coding

Imagine a plot with:

  • no units,
  • a rainbow palette,
  • 217 country labels,
  • a linear income axis,
  • a fitted line,
  • the title “Income saves children.”

Identify the first three changes you would make and the risk each change addresses.

The grammar of graphics

ggplot(data = health_analysis,
       mapping = aes(x = log_income,
                     y = under5_mortality)) +
  geom_point()
  • Data supplies observations.
  • Aesthetics map variables to visual properties.
  • Geometries draw marks.
  • Scales translate data values into positions, colors, and sizes.

Map or set?

# Map a variable to color
aes(color = income_level)

# Set every point to one color
geom_point(color = "#a51c30")

Mapping creates a legend because color carries information.

Choose the encoding

Year
Income level
Under-five mortality
Country name

Which belongs on position, color, facet, or label? Which should not be encoded at all in the first plot?

Build one layer at a time

p <- ggplot(
  health_analysis,
  aes(log_income, under5_mortality)
)

p + geom_point()

Run after every meaningful layer. A plot object can be inspected and extended.

Improve the points

p +
  geom_point(
    aes(color = income_level),
    alpha = 0.45,
    size = 1.8
  )

Opacity reduces overplotting. Color introduces a grouped comparison.

Label the evidence

p +
  geom_point(aes(color = income_level), alpha = 0.45) +
  labs(
    title = "Income and under-five mortality",
    subtitle = "Observed country-years, 2000-2022",
    x = "Log GDP per capita, PPP",
    y = "Deaths per 1,000 live births",
    color = "Income level"
  )

Why log income?

GDP per capita is strongly right-skewed. Equal dollar changes do not have equal meaning across the distribution.

health <- health |>
  mutate(log_income = log(gdp_per_capita_ppp))

A log transformation changes the scale and coefficient interpretation. It does not repair measurement or design problems.

Predict the transformation

Two countries have GDP per capita of 1,000 and 10,000. Two others have 10,000 and 100,000.

On a log scale, which pair is farther apart?

  • A. First pair
  • B. Second pair
  • C. Equal distance
  • D. Depends on the log base

Add a fitted line

p +
  geom_point(aes(color = income_level), alpha = 0.45) +
  geom_smooth(
    method = "lm",
    formula = y ~ x,
    se = FALSE,
    color = "#151515"
  )

The line summarizes a fitted relationship. It is not a policy mechanism.

What does the line hide?

  • Different years.
  • Repeated observations from the same economy.
  • Unequal coverage.
  • Income groups.
  • Regional patterns.
  • Measurement error.
  • Possible nonlinear relationships.

Which hidden feature could change the story most for your policy track?

Facet with purpose

p +
  geom_point(alpha = 0.35) +
  facet_wrap(~ income_level) +
  labs(x = "Log GDP per capita, PPP",
       y = "Deaths per 1,000 live births")

Faceting supports within-group comparisons but can reduce direct across-group comparison.

Plot checkpoint

With a partner, improve one plot for your track:

  1. Name the sample.
  2. Use one intentional encoding.
  3. Label both units.
  4. Write a factual title.
  5. Add one caption limitation.

8 MINUTES

From picture to model

fit <- lm(
  under5_mortality ~ log_income + income_level,
  data = health_analysis
)

summary(fit)

The model summarizes conditional associations in the analysis sample.

Read the formula

outcome ~ predictor + comparison group
  • Left side: what varies.
  • Right side: variables used to summarize that variation.
  • +: included together, not added arithmetically.
  • Reference group: the omitted category used for comparison.

Coefficients have units

For a model with log income:

A one-unit increase in log GDP per capita is associated with an estimated change of β deaths per 1,000 live births, comparing observations at the same included income level.

The sentence must use the coefficient actually produced by the model.

Interpret or overreach?

A
Higher observed income is associated with lower mortality in this sample.
B
Increasing national income will reduce mortality by the fitted amount.
C
The coefficient summarizes a conditional linear relationship.
D
Income level explains every cross-country difference.

Which statements are defensible from this model alone?

The claim ladder

Level Example Supported here?
Descriptive Mortality differs across observed country-years. Yes
Associational Income and mortality are related in the sample. Yes
Predictive The model predicts outcomes for held-out cases. Not yet tested
Causal Raising income causes mortality to fall. No

Residuals are unanswered cases

health_model <- health_analysis |>
  mutate(
    fitted = predict(fit),
    residual = resid(fit)
  )

A residual is observed minus fitted. Large residuals identify observations the model summarizes poorly.

Inspect a surprise

health_model |>
  arrange(desc(abs(residual))) |>
  select(country, year, under5_mortality,
         fitted, residual) |>
  slice_head(n = 8)

Surprise can reveal a data problem, an omitted factor, or a genuinely unusual case.

Diagnose in context

For a surprising observation, check:

  1. Indicator definition and unit.
  2. Missing or imputed inputs.
  3. Duplicate keys.
  4. Country and year context.
  5. Whether the transformation is valid.
  6. Whether the model form is too simple.

Do not delete an observation merely because it is inconvenient.

Two different agent reviews

Code review

  • sample construction,
  • missingness,
  • transformations,
  • labels,
  • reproducibility.

Statistical review

  • units,
  • specification,
  • residuals,
  • claim strength,
  • limitations.

A second reader, not a judge

The agent should look for traceable mismatches:

  • the question names one population but the code filters another,
  • the caption names all countries but the model drops missing rows,
  • the coefficient is described in the wrong units,
  • the prose says “caused” while the design is associational.

“This analysis seems biased” is not yet a finding.

Weak review prompt

Is my analysis correct?

The question is too broad. The agent may rewrite the analysis, mix code with interpretation, or offer generic praise.

Bounded review prompt

Goal: Review this analysis in two separate passes.
Context: Read the policy question, dictionary, script, and figure.
Pass 1: Check code behavior, sample, missingness, labels, and reproducibility.
Pass 2: Check whether the claim matches the descriptive design and output.
Constraints: Do not edit. Quote evidence for each concern. Do not suggest causal language.
Done when: Issues are classified as verified, uncertain, or rejected,
with an R check I can run for each.

Accept no untraceable critique

For every agent concern, ask:

  • Which file or output supports it?
  • Which line or value should I inspect?
  • What R check can confirm it?
  • Is the concern about code, data, statistics, or writing?
  • What evidence would make me reject the concern?

Counterexample hunt

Find one:

  • country-year,
  • subgroup,
  • period,
  • scale,
  • or defensible specification

that makes the headline less complete.

Does it overturn the claim, narrow it, or identify the next question?

4 MINUTES

One-minute claim

Write two sentences:

  1. The strongest result your current evidence supports.
  2. The most important reason not to overinterpret it.

Exchange with a partner and underline any causal verb.

5 MINUTES

Lab 3 begins next

COMPARISON STUDIO · 3-4 PM

Your personal evidence bundle will contain:

  • one clear question,
  • one named analysis sample,
  • one labeled figure,
  • one simple model,
  • one diagnostic check,
  • one supported claim,
  • one limitation,
  • one reviewed agent concern.

Exit ticket

Complete three sentences:

  1. A graph becomes an argument when…
  2. A coefficient does not tell me…
  3. The first observation I want to inspect is…

3 MINUTES