Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Beyond the Prompt: Programming as a Pathway to ...

Beyond the Prompt: Programming as a Pathway to Statistical Thinking

AI tools can now generate a polished ggplot in seconds, scaffold an entire Quarto report from a prompt, and debug a dplyr pipeline faster than most students can type. This raises a reasonable question for statistics and data science educators: should we still be asking students to learn to do these things themselves?

I argue yes — and that the case for teaching programming and reproducible workflows is stronger than ever. When AI produces code freely, mastery becomes the differentiator: the ability to read a generated script and assess whether it is doing what you think it is doing, to recognize when a pipeline quietly produces the wrong answer, and to structure an analysis so that every step can be followed and verified. Beyond code correctness, learning modern data science workflows teaches students to think with data — to ask sharper questions, notice what a dataset can and cannot answer, and build the habits of mind that underpin rigorous reasoning about uncertainty and statistical modeling. These are the foundations of statistical thinking; the code is how we practice them. Reproducibility, in this framing, is not a technical nicety but a cornerstone of scientific integrity — and tools like version control that enforce it turn out to be essential for working productively with AI as well.

Drawing on experience designing introductory data science courses and curricula, this talk presents a case for reframing programming instruction in the AI era — not as syntax acquisition, but as a pathway to statistical thinking. It features concrete code and workflow examples alongside ideas for assignments and assessments designed for a moment when a plausible-looking answer is only a prompt away, and where the real pedagogical challenge is crafting tasks that reward genuine understanding over fluent generation. The goal is not to keep AI out of the classroom, but to produce graduates who can do more than write a good prompt and hope for the best.

Avatar for Mine Cetinkaya-Rundel

Mine Cetinkaya-Rundel

July 12, 2026

More Decks by Mine Cetinkaya-Rundel

Other Decks in Education

Transcript

  1. > Should we still ask students to learn to do

    these things themselves? > Yes.
  2. Student names and scores are in two different files: roster.csv

    and scores.csv. Create a single data frame with student IDs and names from the roster file and the scores for these students from the scores file. roster <- read_csv("data/roster.csv") roster # A tibble: 102 × 2 id name <dbl> <chr> 1 1 Micah Das 2 2 Zara Wright 3 3 Jayden Reed 4 4 Samira Mitchell 5 5 Isla Thomas 6 6 Celine Smith 7 7 Asha Anderson 8 8 Isaac Ndlovu 9 9 Lucas Sato 10 10 Max Nelson # ℹ 92 more rows scores <- read_csv("data/scores.csv") scores # A tibble: 203 × 2 id score <dbl> <dbl> 1 1 83 2 2 75 3 3 50 4 4 70 5 5 74 6 6 88 7 7 50 8 8 81 9 9 79 10 10 75 # ℹ 193 more rows
  3. 🤖 With AI : roster |> left_join(scores, join_by(id), relationship =

    "many-to-many") # A tibble: 105 × 3 id name score <dbl> <chr> <dbl> 1 1 Micah Das 83 2 2 Zara Wright 75 3 3 Jayden Reed 50 4 4 Samira Mitchell 70 5 5 Isla Thomas 74 6 6 Celine Smith 88 7 7 Asha Anderson 50 8 8 Isaac Ndlovu 81 9 9 Lucas Sato 79 10 10 Max Nelson 75 # ℹ 95 more rows Create a single data frame with student IDs and names from the roster file and the scores for these students from the scores file. ↑ Sonnet 4.6 (via Posit AI )
  4. 👩💻 Without AI : roster |> left_join(scores, join_by(id)) Warning message:

    In left_join(roster, scores, join_by(id)) : Detected an unexpected many-to-many relationship between `x` and `y`. ℹ Row 21 of `x` matches multiple rows in `y`. ℹ Row 54 of `y` matches multiple rows in `x`. ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this warning. # A tibble: 105 × 3 id name score <dbl> <chr> <dbl> 1 1 Micah Das 83 2 2 Zara Wright 75 3 3 Jayden Reed 50 4 4 Samira Mitchell 70 5 5 Isla Thomas 74 6 6 Celine Smith 88 7 7 Asha Anderson 50 8 8 Isaac Ndlovu 81 9 9 Lucas Sato 79 10 10 Max Nelson 75 # ℹ 95 more rows
  5. 👩💻 Without AI : roster |> slice(21) # A tibble:

    1 × 2 id name <dbl> <chr> 1 21 Owen Rossi scores |> filter(id == 21) # A tibble: 4 × 2 id score <dbl> <dbl> 1 21 83 2 21 85 3 21 80 4 21 84 scores |> slice(54) # A tibble: 1 × 2 id score <dbl> <dbl> 1 51 64 roster |> filter(id == 51) # A tibble: 3 × 2 id name <dbl> <chr> 1 51 Grace Baker 2 51 Grace Baker 3 51 Elliott Rundel
  6. 🤖 With AI : Can you explain what it means

    to allow for “many-to-many ” relationships? ↑ Many-to-many means the join key is not unique in either table, so each matching key produces a Cartesian product of matching rows (rows multiply). dplyr warns by default because this often indicates a mistake; setting relationship = "many-to- many" tells dplyr you expect that behavior and to proceed. Sonnet 4.6 (via Posit AI )
  7. 🤖 With AI : […] Two data quirks worth knowing

    about, since they're why the result has 105 rows instead of ~ 100 (dplyr will also print a "many-to-many relationship" warning about them): 1. Student 21 has four different scores in scores.csv ( 83, 85, 80, 84 ) , so that student appears four times in the joined result. If these are repeated attempts and you want one row per student, you could average them first. 2. The roster has duplicate entries for id 51 — "Grace Baker" listed twice, plus a conflicting "Elliott Rundel" under the same id. roster % > % distinct() removes the exact duplicate, but the Baker/Rundel conflict is something you'd need to resolve based on which record is correct. If the duplicates are intentional (e.g., multiple test attempts), the plain left_join above is all you need — you can silence the warning by adding relationship = "many- to-many" to the join. Fable 5 Create a single data frame with student IDs and names from the roster file and the scores for these students from the scores file. ↑
  8. 🤖 With AI : Create a single data frame with

    student IDs and names from the roster file and the scores for these students from the scores file. ↑ Opus 4.8 (via Posit AI )
  9. 🤖 With AI : ggplot(mtcars, aes(x = disp, y =

    mpg)) + geom_point() + geom_smooth(method = "lm", se = FALSE) Create a scatterplot of miles per gallon (mpg) vs. displacement (disp) in mtcars, and overlay the regression line for predicting mpg from disp. ↑ Opus 4.8 (via Posit AI )
  10. 🤖 With AI : ggplot(mtcars, aes(x = disp, y =

    mpg)) + geom_point() + geom_smooth(method = "lm", se = FALSE) Create a scatterplot of miles per gallon (mpg) vs. displacement (disp) in mtcars, and overlay the regression line for predicting mpg from disp. ↑ Opus 4.8 (via Posit AI )
  11. 🤖 With AI : mtcars |> filter(disp < 200) |>

    ggplot(aes(x = disp, y = mpg)) + geom_point() + geom_smooth(method = "lm", se = FALSE) Now limit to cars with displacement below 200. ↑ Opus 4.8 (via Posit AI )
  12. 🤖 With AI : The poisson.csv file contains data on

    count observations, along with two features: one numerical and the other categorical. Fit a Poisson regression model to these data with R. Report the estimates for the regression coefficients you obtain and interpret them in the context of the data. ↑ GPT - 5.5 Medium dat <- read.csv("poisson.csv") fit <- glm( y ~ x1 + factor(x2), family = poisson(link = "log"), data = dat ) summary(fit) exp(coef(fit)) Term Estimate Exp(estimate) Interpretation Intercept 1.5579 4.7490 For observations with x2 = a and x1 = 0, the expected count is about 4.75. x1 - 0.1939 0.8238 A one-unit increase in x1 multiplies the expected count by 0.824, or decreases it by about 17.6%, holding x2 fixed. x2 = b - 0.0441 0.9569 Observations with x2 = b have an expected count about 4.3% lower than those with x2 = a, holding x1 fixed.
  13. 🤖 With AI : Check if this model is a

    good fit. ↑ GPT - 5.5 Medium […] The conclusion is that the simple Poisson regression captures some mean structure, but it does not adequately model the variability in the count response. A better next step would be to fit either a quasi-Poisson model or a negative binomial regression. In this dataset, a negative binomial model gives much better evidence of handling the extra-Poisson variation.
  14. 👩💻 Without AI : The poisson.csv file contains data on

    count observations, along with two features: one numerical and the other categorical. Fit a Poisson regression model to these data with R. Report the estimates for the regression coefficients you obtain and interpret them in the context of the data.
  15. Level the playing field with explicit “how to best AI

    ” instruction ✅ Small periodic doses, not a single lecture. ✅ Explicit examples for proper, sufficient citation, not expecting students get it right. ✅ Provide AGENTS.md files and/or system prompts for properly calibrated interactions with AI tools.
  16. ✅ Small periodic doses, not a single lecture. ✅ Explicit

    examples for proper, sufficient citation, not expecting students get it right. ✅ Provide AGENTS.md files and/or system prompts for properly calibrated interactions with AI tools. ✅ Expect to update each semester, if not more often. Level the playing field with explicit “how to best AI ” instruction ✅ Also teach the what, the how, and the ethical considerations.
  17. This analysis neglected that some values in the raw data

    are in parentheticals, meaning they're negative values. Fix this issue in the Quarto file. ↑ Emphasize workflow best practices, especially version control
  18. 🔲 Increasing number of students use AI tools as first

    step, before thinking about how to approach a task, can I wiggle my way in there? 🔲 Thanks to large numbers of students and TAs, and thanks to Gradescope, I ’ m already writing the darn detailed rubrics! 🔲 Can AI help TAs redistribute their time towards higher-value (and more enjoyable!) touch points and away from repetitive (and error-prone) tasks much of which go unread? 🔲 Neither the TAs nor I want to provide detailed feedback to answers generated solely by AI. Shift AI use from taking shortcuts, towards supporting learning
  19. library(ellmer) library(glue) library(tidyverse) prompt <- function(question, rubric_detailed, rubric_simple, answer){ chat

    <- chat_openai( system_prompt = "You are a helpful course instructor teaching a course on data science with the R programming language and the tidyverse and tidymodels suite of packages. You like to give succinct but precise feedback." ) chat$chat( glue( "Carefully read the {question} and the {rubric_detailed}, then evaluate {answer} against the {rubric_detailed} to provide feedback. Format the feedback as bullet points mapping to the bullet points in the {rubric_simple}.” ) ) } Shift AI use from taking shortcuts, towards supporting learning
  20. David Spiegelhalter Professor, University of Cambridge “There is no substitute

    for simply looking at data properly.” FROM “THE ART OF STATISTICS ”