AEM 6850
Empirical Methods for Applied Economists
Prof. Ariel Ortiz-Bobea
Thursday, September 17, 2026
By the end of this session you can:
The next word is picked (statistically) from a list of candidates. The next word is not retrieved from a file or data.
| Fact | Consequence | Habit | The check you perform |
|---|---|---|---|
| Predicts the next word | Plausible but incorrect | Check claim against truth | Read the explanation against the code |
| Generates, does not look up | Made up references and arguments | Double check | Check DOI link |
| Not deterministic | Two runs, two answers; nothing to re-run | Ask for the script, not the answer | Run the script; read what it prints |
| Finite memory of a chat | Long chats degrade | One task per chat; plan | A new chat for each task |
| Tier | Example | It sees and can |
|---|---|---|
| Chat | claude.ai, ChatGPT, Gemini | what you paste; returns text |
| Assistant | Cowork (desktop app) | a folder; reads, runs, writes files |
| Coding agent | Claude Code, Codex | a repository; edits as diffs |
Can
Cannot
Make the folder now (~ a couple of mins). Everything today reads one file, gapminder.csv.
gapminder-practice, as a subdirectory of your github folder. Create Project.gapminder-practice/ an RStudio project in your github folder
gapminder-practice.Rproj open this, so data/gapminder.csv resolves
data/gapminder.csv the one input: 1704 rows, 6 columns
code/ one script per task (empty for now)
output/ appears when a script writes there
gapminder R package (Jenny Bryan), data from gapminder.org, CC BY 4.0.country, continent, year, lifeExp, pop, gdpPercap.d <- read.csv("data/gapminder.csv"). Nothing ever edits the file; scripts write to output/.Prompt to paste into Claude
I ran the R line below and got the error under it. Tell me what the
error means and which part of my line caused it. Do not give me a
different approach. Fix only the line I wrote.
tapply(d07$lifeexp, d07$continent, mean)
Error in tapply(d07$lifeexp, d07$continent, mean) :
arguments must have same lengthd07 holds the 2007 rows of gapminder.csv. Its column is lifeExp, capital E.code/1_lifeexp_2007.R
# 1_lifeexp_2007.R: mean life expectancy by continent in 2007.
# Reads data/gapminder.csv; writes output/lifeexp_2007.csv.
YEAR <- 2007
d <- read.csv("data/gapminder.csv")
stopifnot(nrow(d) == 1704)
d07 <- d[d$year == YEAR, ]
m <- tapply(d07$lifeExp, d07$continent, mean)
print(round(m, 2))
out <- data.frame(continent = names(m), mean_lifeExp = round(m, 2))
dir.create("output", showWarnings = FALSE)
write.csv(out, "output/lifeexp_2007.csv", row.names = FALSE)80.72.tapply().| country | lifeExp | pop |
|---|---|---|
| Australia | 81.235 | 20,434,176 |
| New Zealand | 80.204 | 4,115,771 |
Prompt to paste into Claude

# Exercise 1 (7 minutes). RStudio open on gapminder-practice; claude.ai
# in your browser, a new chat.
#
# 1. RStudio: File > New File > R Script. Paste the script from the
# slide "code/1_lifeexp_2007.R, the given script". File > Save As,
# into the code folder, named 1_lifeexp_2007.R. Click Source. Read
# the five numbers in the Console.
#
# 2. claude.ai: paste the explain prompt from the slide "Asking for an
# explanation of code". Shift+Enter. Paste the whole script under
# it. Enter.
#
# 3. Read the reply one sentence at a time. For each sentence, find the
# line it describes. Mark any sentence that says something the line
# does not do, and any line no sentence covers.
#
# 4. Find the sentence about the tapply() line. Does it say what the
# Oceania number averages over: two countries, or 24.5 million people?
# Decide from the code and the two rows on the slide.
#
# Two answers to compare with the room: the sentence you marked, and
# whether your explanation said each country counts once (80.72) or
# described the continent's population.The tapply() line averages the countries, each once: Oceania 80.72. A sentence that says “the continent’s life expectancy” or “weighted by population” describes a different computation. stopifnot() does more than “check”: it stops the script if the count is not 1,704.
Prompt to paste into Claude, web search off

| Failure | Comes from | Gapminder version |
|---|---|---|
| Confident but wrong | Fact 1: plausible before correct | “Oceania’s 2007 life expectancy was 80.7 years”, from memory |
| Made up references | Fact 2: generates, does not look up | three papers with DOIs, one of which resolves to nothing |
| Plausible-but-wrong recipe | Facts 1 and 2 | a mean over all twelve years when you meant 2007 |
str(d) in the message fixes it.| Prompt | What comes back |
|---|---|
| Basic: the question alone | five numbers, from memory |
With pasted context: str(d), head(d, 3), “base R only” |
one line of code that fits your columns |
Asking for a script at code/..., saying what it reads and what it prints |
a file you can run and keep |
The third one is the only one you can re-run tomorrow.

Prompt to paste into Claude, with gapminder.csv attached
The attached file is gapminder.csv: one row per country and year; columns
country, continent, year, lifeExp, pop, gdpPercap; 1704 rows; 142
countries; years 1952 to 2007 every five years. In my RStudio project it
sits at data/gapminder.csv.
Goal: the population-weighted mean of lifeExp for each continent in 2007.
Write an R script I will save as code/2_lifeexp_weighted_2007.R. Base R
only, no packages. Read the file with read.csv("data/gapminder.csv"). Keep
the rows for 2007 first; the file has twelve years per country. For each
continent compute sum(lifeExp * pop) / sum(pop); do not take the simple
mean across countries. Write output/lifeexp_weighted_2007.csv with the
columns continent and weighted_lifeExp, and end with cat() lines that
print the five values rounded to two decimals.
Do not compute the numbers here and do not paste a table; I will run the
script myself. Do not invent columns. If the file does not match what I
described, stop and say so.| The answer, in a chat | The script, in code/ |
|
|---|---|---|
| Re-run tomorrow, elsewhere, by someone else | no | yes |
| Read line by line when a number looks wrong | nothing to read | yes |
| Fits in a commit | no | yes |
| The numbers come from your file | not necessarily | always |
Ask for the script, not the answer. The research artifact is always code.
# Exercise 2 (9 minutes). claude.ai, a new chat; RStudio open on
# gapminder-practice.
#
# 1. Click + at the left of the message box, choose Add files or
# photos, pick gapminder-practice/data/gapminder.csv. Wait for the
# file chip.
#
# 2. Paste the prompt from the slide "Asking for the script: the
# prompt", unchanged. Enter.
#
# 3. Read the reply. A script, a number, or both? If it gave numbers, it
# did not do what you asked: reply "You computed it; I asked for the
# script. Give me only the R script." and wait.
#
# 4. Copy the R script: a code block has a copy icon at its top right;
# a card named after the script opens a panel with a Copy button.
# Not a Python block. RStudio: File > New File > R Script, paste,
# File > Save As into code/ as 2_lifeexp_weighted_2007.R. Click
# Source. Read the five values.
#
# 5. Before you trust Oceania, in the Console:
# (81.235 * 20434176 + 80.204 * 4115771) / (20434176 + 4115771)
# Does the script agree, to two decimals?
#
# Two answers to compare with the room: the Oceania value your script
# printed, and the line in your script that does the weighting.A script, and Oceania prints 81.06, the same as the Console: 81.06215. The weighting line is two tapply() sums divided, or weighted.mean(lifeExp, pop), or sum(lifeExp * pop) / sum(pop) inside a function. All three give the same number.
| Chat | Cowork | |
|---|---|---|
| How context arrives | you paste it | from the folder you connect |
| Reads your files | no | every file in the folder |
| Runs code in a sandbox | Python, on an attached file | yes, on Anthropic’s servers |
| Runs your R script | no | no; you run it in RStudio |
| Edits your files | no; you paste its output | yes, directly |
| What you read afterwards | the script you pasted | git status and git diff |
| Forces “ask for the script” | yes, by accident | no; the prompt must |
git status, then git diff, then open every new file.Prompt (live demo, chat)
Write an R script that reads the life expectancy table on the Wikipedia
page https://en.wikipedia.org/wiki/List_of_countries_by_life_expectancy
with the rvest package and draws a world map coloured by life expectancy
with the maps package. Save the map to output/lifeexp_map.png. I will run
the script in RStudio. Give me the script only.Prompt (live demo, Cowork)
Working in this folder, an RStudio project. Write an R script at
code/lifeexp_map.R that reads the life expectancy table on the Wikipedia
page https://en.wikipedia.org/wiki/List_of_countries_by_life_expectancy
with the rvest package and draws a world map coloured by life expectancy
with the maps package, saved to output/lifeexp_map.png. Fetch the page
yourself and read the table's columns before you write the script. Base R
plus rvest and maps, nothing else. Do not run the script; I will run it
in RStudio. Do not change any existing file. Create output/ if it is
missing. Report the one file you wrote, then stop.code/lifeexp_map.R appears in the folder; git status shows it.
Tested 2026-09-15: figures/08-ai-chat/demo/wiki_lifeexp_map.R, on the session page. 227 countries and territories, 214 drawn; the rest grey.
AI disclosure, at the end of the note
AI disclosure, filled in
## AI disclosure
- Tools: Claude Sonnet 5 at claude.ai (free plan), September 2026.
- Used for: writing code/2_lifeexp_weighted_2007.R from a prompt that gave
the columns, the 2007 filter and the weighting; explaining
code/1_lifeexp_2007.R line by line.
- Checked: sourced both scripts from a fresh R session; Oceania 2007 gives
80.72 unweighted and 81.06 weighted, matching a hand calculation from the
Australia and New Zealand rows. One sentence of the explanation described
the tapply() mean as population-weighted; the code counts each country once.Nine exercises on the session page, with solutions behind a click. All of them run in a chat you can throw away and in gapminder-practice, never in a graded repository.
Ask for the script, not the answer. The research artifact is always code.
Every prompt shape, every check and the disclosure block from today, one line each, on the session page, with the things that bite.
AEM 6850 · Session 8