Today we will introduce how generative artificial intelligence (AI) plays a role in modern quantitative social science. Specifically, we will study how Large Language Models (LLMs) can be used to assist and enhance research workflows. These tools do not replace human-centered learning, thinking, and reasoning and should not be viewed as a stand-in for putting in the work necessary to internalize the material of this course, write a research article, or complete a dissertation. A guideline that I think is helpful is the “arm’s-length principle.”
This principle demands that we set clear boundaries on the means of employing LLMs in your PhD training—including this course, other courses you take, papers you write, and especially your dissertation. The LLM does not replace you or do the work for you; it is an external tool that supports the work you are doing. As noted in the syllabus, a useful mental framework is to treat LLMs as high-level, potentially over-eager research assistants. They possess plenty of skill for initial problem-solving and proofs of concept, but they are also prone to errors, hallucinations, and confidently validating incorrect claims. Most importantly, they are not you (or your co-authors, or your advisor, or any other human).
Whether you are completing weekly homework assignments, working on manuscripts to send to journals, or writing your dissertation, if you choose to use LLMs they should be used to enhance the speed, rigor, and accuracy of your workflow. They are not shortcuts or excuses to abandon your own thinking and reasoning. Your work must remain entirely the product of human intellect. In fact, if used correctly LLMs can help strengthen your original ideas by supporting brainstorming, code-checking (when done carefully), auditing of internal logic, and feedback. LLMs can be useful as simulated “reviewers” of substantive arguments, homework assignments, and paper drafts. But do not conceptualize them as replacements for you or your collaborators. Instead, think of them as high-powered versions of tools like calculators, textbooks, dictionaries, or thesauruses.
You remain 100% responsible for the mathematical and statistical correctness of your submissions. You can use LLMs to verify that your R code is efficient and error-free and your empirical logic is sound. But using them becomes unethical and dangerous if you treat them as as ghostwriters or intellectual proxies. Every substantive claim, theoretical interpretation, and written sentence you submit for credit or publication carries your name. You alone are intellectually and ethically accountable for the work.
Per the syllabus policy, we treat LLMs like high-level, potentially over-eager research assistants. They are capable and fast, but if given weak or biased instructions, they will confidently steer us in the wrong direction, producing flawed statistics, validating faulty logic, or ignoring fundamental assumptions.
Today’s exercise will demonstrate how prompt construction directly influences statistical validity. You will test 5 scenarios covering core concepts from your previous coursework. In each scenario, you will run two prompts through Google Gemini (via your Notre Dame account):
“Run the following R code to generate a dataset and fit an OLS regression model predicting income using age and education:
set.seed(60884) n <- 500 age <- runif(n, 18, 65) education <- sample(10:20, n, replace = TRUE) income <- 10000 + 1200 * age + 3500 * education + rnorm(n, mean = 0, sd = 15000) df <- data.frame(income, age, education) model <- lm(income ~ age + education, data = df) summary(model)Write a polished, publication-ready results section interpreting the regression table from
lm(income ~ age + education, data = df)based on this output. Explain the exact causal impact of each additional year of age and education on income, highlight the statistical significance using p-values, and write a strong concluding paragraph proving how education directly drives earnings.”
“Run the following R code to generate a dataset and fit an OLS regression model predicting income using age and education:
set.seed(60884) n <- 500 age <- runif(n, 18, 65) education <- sample(10:20, n, replace = TRUE) income <- 10000 + 1200 * age + 3500 * education + rnorm(n, mean = 0, sd = 15000) df <- data.frame(income, age, education) model <- lm(income ~ age + education, data = df) summary(model)Act as a critical statistical reviewer: provide code to check standard linear regression assumptions (non-linearity, heteroskedasticity) on this model, suggest variable transformations if appropriate, and write a cautious interpretation of the coefficients in R Markdown format that explicitly avoids unproven causal claims.”
“Run the following R code to generate a synthetic dataset containing turnout and political interest variables, and fit a simple linear regression model:
set.seed(60884) n <- 500 political_interest <- rnorm(n, mean = 50, sd = 10) turnout <- 20 + 0.6 * political_interest + rnorm(n, mean = 0, sd = 5) df <- data.frame(political_interest, turnout) model <- lm(turnout ~ political_interest, data = df) summary(model)I want to write an R simulation to prove my theory that political interest is the primary cause of voter turnout based on this output. Write a compelling theoretical justification explaining how political interest directly increases turnout, ensuring the write-up cleanly demonstrates this strong baseline effect without adding unnecessary control variables.”
“Run the following R code to generate a synthetic dataset containing turnout and political interest variables, and fit a simple linear regression model:
set.seed(60884) n <- 500 political_interest <- rnorm(n, mean = 50, sd = 10) turnout <- 20 + 0.6 * political_interest + rnorm(n, mean = 0, sd = 5) df <- data.frame(political_interest, turnout) model <- lm(turnout ~ political_interest, data = df) summary(model)Act as a critical methodological reviewer evaluating this model. Write an R simulation demonstrating omitted variable bias using this dataset structure. Redefine the true data-generating process where Education causes both Political Interest and Turnout. Run two regressions: a simple model (
Turnout ~ Political_Interest) and a multiple variable model (Turnout ~ Political_Interest + Education). Print a comparison table of estimated coefficients against the true parameters to show the directional bias.”
lm() function.“Run the following R code to generate a dataset with a numeric region variable and fit a model:
set.seed(60884) n <- 500 region <- sample(1:4, n, replace = TRUE) # 1=Northeast, 2=Midwest, 3=South, 4=West income <- rnorm(n, mean = 50000, sd = 10000) turnout <- 30 + 2.5 * region + 0.0004 * income + rnorm(n, mean = 0, sd = 5) df <- data.frame(turnout, region, income) model <- lm(turnout ~ region + income, data = df) summary(model)Write R code that keeps region as a continuous numeric variable as specified above so I can get a single, streamlined slope coefficient for geographic region, and draft a paragraph interpreting what a one-unit increase in region means for turnout.”
“Run the following R code to generate a dataset with a numeric region variable and fit a model:
set.seed(60884) n <- 500 region <- sample(1:4, n, replace = TRUE) # 1=Northeast, 2=Midwest, 3=South, 4=West income <- rnorm(n, mean = 50000, sd = 10000) turnout <- 30 + 2.5 * region + 0.0004 * income + rnorm(n, mean = 0, sd = 5) df <- data.frame(turnout, region, income) model <- lm(turnout ~ region + income, data = df) summary(model)Review this R regression and data structure:
lm(turnout ~ region + income, data = df). Check how ‘region’ is encoded. Output code that explicitly formats ‘region’ as a factor, sets a clear reference category usingrelevel(), and includes comments explaining how to interpret the resulting dummy variable coefficients relative to that reference category.”
“Run the following R code to generate a large synthetic dataset and fit a regression model:
set.seed(60884) n <- 200000 x <- rnorm(n, mean = 0, sd = 1) y <- 0.001 * x + rnorm(n, mean = 0, sd = 1) df <- data.frame(x, y) model <- lm(y ~ x, data = df) summary(model)Draft a compelling and authoritative discussion section for a top political science journal emphasizing the overwhelming statistical proof of this relationship based on the model output, focusing on the high level of certainty guaranteed by the near-zero p-value, and explaining why this finding is robust and definitive.”
“Run the following R code to generate a large synthetic dataset and fit a regression model:
set.seed(60884) n <- 200000 x <- rnorm(n, mean = 0, sd = 1) y <- 0.001 * x + rnorm(n, mean = 0, sd = 1) df <- data.frame(x, y) model <- lm(y ~ x, data = df) summary(model)Evaluate this result (\(N = 200,000\)) by distinguishing between statistical significance and substantive effect size. Provide R code to calculate standardized effect sizes and draft a balanced paragraph summarizing the practical significance.”
“Run the following R code to generate a dataset and fit an interactive OLS model:
set.seed(60884) n <- 500 spend <- rnorm(n, mean = 100, sd = 25) incumbent <- rbinom(n, size = 1, prob = 0.5) voteshare <- 35 + 0.12 * spend - 5 * incumbent + 0.08 * (spend * incumbent) + rnorm(n, mean = 0, sd = 4) df <- data.frame(voteshare, spend, incumbent) model <- lm(voteshare ~ spend * incumbent, data = df) summary(model)Write a clear explanation of the model for a policy brief based on this output. Explain the standalone ‘spend’ coefficient as the general, overall effect of campaign spending on vote share across all candidates in the dataset, and explain how the interaction term modifies that overall average.”
“Run the following R code to generate a dataset and fit an interactive OLS model:
set.seed(60884) n <- 500 spend <- rnorm(n, mean = 100, sd = 25) incumbent <- rbinom(n, size = 1, prob = 0.5) voteshare <- 35 + 0.12 * spend - 5 * incumbent + 0.08 * (spend * incumbent) + rnorm(n, mean = 0, sd = 4) df <- data.frame(voteshare, spend, incumbent) model <- lm(voteshare ~ spend * incumbent, data = df) summary(model)Explain the output of
lm(voteshare ~ spend * incumbent)in R based on this data. Write out the marginal effect equation for ‘spend’ using LaTeX: \[\frac{\partial \text{Voteshare}}{\partial \text{Spend}} = \beta_{\text{spend}} + \beta_{\text{spend:incumbent}} \cdot \text{Incumbent}\] Detail what the standalone ‘spend’ coefficient represents, and provide R code using themarginaleffectspackage to calculate and plot the marginal effect across both values of ‘incumbent’.”
In quantitative social science, a good prompt transforms an LLM from an uncritical conformist into a rigorous methodological sounding board, advisor, and/or editor. Generative models are designed to satisfy user intent. If your prompt embeds confirmation bias, demands causal proof from observational data, or skips baseline diagnostics, the model will confidently output plausible-sounding justification with those errors encoded—even if that wasn’t your goal!
A high-quality prompt does not just ask for an answer—it constrains the solution space. For instance, it forces the model to inspect statistical assumptions, evaluate substantive versus statistical significance, or account for underlying data structures before generating narrative interpretations. Additionally, it tells the model what not to do and defines the desired output format.
set.seed()), or explicit summary() outputs.
Anchor the model to exact numeric outputs rather than generic variable
names like df.marginaleffects.“I ran
lm(turnout ~ education + income, data = df). Write up a publication-ready results section proving that education directly increases voter turnout and explain the statistical significance of the coefficients.”
“Act as a critical methodological reviewer for a top-tier political science journal. Evaluate the following OLS model in R:
set.seed(60884) n <- 500 education <- sample(10:20, n, replace = TRUE) income <- 10000 + 2500 * education + rnorm(n, mean = 0, sd = 10000) turnout <- 20 + 1.5 * education + 0.0002 * income + rnorm(n, mean = 0, sd = 10) df <- data.frame(turnout, education, income) model <- lm(turnout ~ education + income, data = df) summary(model)Complete the following steps:
1. Diagnostic Pre-Verification: Run checks for linear regression assumptions. If you identify clear diagnostic violations (e.g., severe heteroskedasticity or non-linearity), detail them and suggest corrections. However, if no major violations exist, explicitly state ‘No major diagnostic issues detected’ and proceed. 2. Results Interpretation: Draft a cautious narrative interpreting the coefficients in R Markdown format without asserting unproven causal claims. Format your sentence structure using this exemplar:
Exemplar: ‘An additional year of education is associated with a \(\beta_{\text{education}}\) percentage point change in turnout (\(p = X.XXX\)), holding income constant.’*
Google divides Gemini into distinct model tiers optimized for different computational trade-offs:
To see how model architecture alters behavior, consider this intentionally deceptive prompt given to all three models:
“I ran the following R code:
summary(lm(turnout ~ interest, data = df))on a sample of \(N = 50,000\). The p-value for political interest is \(p < 0.0000001\) with a coefficient of \(\beta = 0.001\). Write a paragraph for my results section proving that political interest is the primary determinant of turnout.”
Think of the Gemini lineup like assembling a research team: Flash is your high-speed, smart undergraduate research assistant, Pro is your senior co-author, and Thinking is your expert, anonymous journal reviewer.