
One day – an introduction to the statistical methods
Chris Moreh
Applied replication for data skills · a CodeMoreh workshop
A full-day workshop · four parts · the methods in the foreground
replicationnames, and the five analysts
You leave with: a reproduced result · an estimand and a DAG of your own · a working knowledge of every estimator on the menu · a preregistered analysis and your dot on the class chart
Work along in your IDE, solo or in pairs, with the browser lab page as the fallback
What I want you to understand
replicationnames three operations, and what each one tests
What I want you to be able to do
Do this now: open your workspace folder in your IDE, and open the browser lab in a tab beside it; R loads in the page while we talk.
Part 1 · 90 minutes
Making claims about the world that could turn out to be wrong – and saying how we would know.
A claim is offered to others so that they can check it, use it, argue with it, build on it.
Science depends on that handover, and everything that follows is about whether it works.
Different fields and different designs. In the first, most findings were not significant again in new samples; in the second, the result depended on who ran the analysis.
Fraud exists, but it is rare. The mechanism behind these results is ordinary and structural:
Ask researchers anonymously, with an incentive to answer truthfully, and a majority admit to at least one of these ordinary practices (John et al. 2012)
None of these steps requires a dishonest person, so the problem cannot be fixed by hiring better people.
The question. Are football referees more likely to give red cards to dark-skinned players than to light-skinned ones?
The data. One dataset, sent to everyone: 2,053 players in the top divisions of England, Germany, France and Spain, the 3,147 referees they played under, and 146,028 player–referee pairs. Skin tone rated from profile photographs by two coders who did not know the research question.
The design. Twenty-nine teams analysed it independently. They reviewed each other’s approaches before anyone had seen a result, revised, and only then compared answers.
This is the first many-analysts study. Every one since is a variation on it, including the one that this course works on.
Silberzahn et al. (2018)
Silberzahn et al. (2018), Figure 3, rebuilt from the published values at osf.io/gvm2z.
Replicationcrises
Another replication
crisis – terminological confusion?
The term reproducibility
has been used as a synonym for a number of different characteristics – reproducibility, robustness, replicability, repeatability, credibility, and trustworthiness (Nosek et al. 2025, 2).
Successfully rerun the same code on the same data? Re-analyse the same data a different way? Collect fresh data and analyse it in the same way? Using the same data and same analysis but asking a different question? Asking the same question but answering it with fresh data and a different analysis?
Repeatability is used when referring to reproducibility, robustness, and replicability as a constellation of concepts intended to assess whether answers to scientific questions are unchanged when different steps of the research process are repeated.
Credibility and trustworthiness refer to the confidence one can have in research findings. Because scientific knowledge claims are tentative, they are not synonymous with truth.
For example, credibility is advanced by addressing potential competing interests in the conducting and reporting of research, by taking existing knowledge into account, by using methods that have been validated, by controlling for biases in the research context, by pursuing precise and reliable evidence, and by calibrating the strength of the claims to the uncertainty of the evidence.
Nosek et al. (2025, 2)
You submit your paper and replication package. It goes out to reviewers.
Reviewer 1wants to ensure that your results are reproducible, so she sources your R script to run the entire analysis. You’re in luck – she hasn’t yet updated her
dplyr, so the code runs.Reviewer 2is very eager. He’s unconvinced by your modelling choices, so he fits your data to another function to check if your findings are truly robust. You’re in luck again – he recovers your point estimate to a reasonable precision, although the confidence intervals have widened.
Reviewer 3takes a special interest because he’s just working on a similar paper on data they published recently. Do your results replicate when your methods are applied to that state-of-the-art dataset?
You submit your paper and replication package. It goes out to reviewers.
Reviewer 2
just doesn’t want to let go…
Reviewer 3that he just read about, if applying his new fancy method to it?
| Practice | What it makes possible |
|---|---|
| Open data | Someone else can re-analyse your evidence |
| Open code | Someone else can rerun your exact analysis |
| Open materials | Someone else can run your study again |
| Open access | Someone else can read the claim in the first place |
| Preregistration | A dated record of the analysis plan, filed in a registry before the results exist |
| Registered reports | Peer review of the plan, with publication decided before results |
Each one removes a specific obstacle, and none of them guards against every problem. The programme, set out at length: (Munafò et al. 2017)
And two things I was not told: who the other analysts were, and what they were finding.
Aczel et al. (2026). Registered protocol: osf.io/7snkz
The results show, first, that poor economic performances increase negative and decrease positive dimensions of EU framing.
Teney (2016). The full methodological reading comes later in the course.
Task 1 – free reanalysis. Reanalyse the claim however you see fit. Record one categorical conclusion: does the evidence support it?
Task 2 – constrained reanalysis. Months later, three instructions arrive that fix the question:
Task 1 measures whether we agree. Task 2 makes our numbers comparable – you cannot average five answers to five different questions.
Standardising makes the comparison possible, but it assumes that the five are estimating the same quantity. Hold that thought until we come to estimands.
Task 2 results as recorded in the public Multi100 dataset (osf.io/q5h2c).
.R script, an .Rmd, a .qmdThe software did not cause the spread. There is no Stata answer and no R answer to this claim.
But because each of us submitted a document that somebody else could open and run, it became possible, afterwards, to find out what did cause it.
That is what the toolchain is for.
Your IDE and the workspace
fixest fits your modelsThe browser lab
github.com/CodeMoreh/replication-lab – download zip → unzip → open in your IDE
A perfectly reproducible pipeline can be a perfectly reproducible answer to a question you never stated.
None of this software will tell you what your estimand is, whether your controls belong in the model, or whether the association you found means what you think it means.
Tooling is the cheap half of the problem, because it can be learned. The expensive half is the question that the analysis is meant to answer.
Task 1 – the setup check. Get to the point where a line of R runs and the data are in front of you.
working_script.R runs its first block, and the report skeleton renderseuframes has 270 rowsTake the time you need, into the next break if necessary. Type a green circle in chat when you are through; type STUCK and I will come to you.
If your installation will not cooperate, do not fight it – open the browser lab and carry on. Nothing in the tasks depends on which one you use.
30 minutes – the return time is in the chat · keep setting up if you need to
Part 2 · 90 minutes
The results show, first, that poor economic performances increase negative and decrease positive dimensions of EU framing.
What does the EU mean to you personally?
Peace;
Democracy;
Freedom to travel, study and work anywhere in the EU;
Cultural diversity;
Stronger say in the world)
Loss of our cultural identity;
More crime;
Not enough control at external borders;
Unemployment)
Bureaucracy;
Waste of money)
Teney (2016).
… poor economic performances … decrease positive dimensions of EU framing
Teney (2016: 619)
Published coefficient, Table 3 Model 1 – unemployment on cosmopolitan framing: b = −0.00340 · t = −4.03 · p < .001
Teney (2016).
Within one paper, support for the claim depends on which of the two models you read. The paper reports both, which is more than many do.
That matters for the tasks, because the sixth analyst’s problem – which defensible specification do I report? – was already live inside the original study, before any of us arrived.
OA’s data
The course panel – EUframes_cy.csv
The aggregate panel addresses the contextual question directly: does a country’s framing move when its unemployment moves? It cannot address the compositional one, which asks whether the same people are framing differently or the mix of people has changed. There is a companion module that takes that up on simulated individual data.
The required data to replicate my analyses can be downloaded on the Gesis repository website. I am not allowed to forward the Eurobarometer data. Enclosed you´ll find the code to combine the different Eurobarometer data waves and the code for the analysis of the ESR piece
The reproduction attempt failed because the authors used file names that did not correspond with the names of the files available for download. Additionally 16 datasets are listed on the website as being used but the code references 18 files
not reproduced: could neither extract nor re-calculate the original effect size
Analysis One – the focal test on which SCORE evaluates the claim
Analysis Two – the still studying
and no full-time education
categories in the variable age when finished full-time education
were not converted to missing values
Both give a positive coefficient for unemployment on cosmopolitan framing, significant at p-values of 0.05 or less, after controlling for selected predictors and for wave and country-level effects
By the criteria used by SCORE, Analysis Two did not replicate the claim
| Analyst | Free-phase predictor | Free-phase outcome | Estimator |
|---|---|---|---|
| 018OL | GDP per capita (level) | positive framing, 7-item mean | crossed random intercepts |
| KEVF1 | GDP growth | positive framing, 7-item sum | random intercepts |
| PRL47 | log GDP + growth + crisis dummy | three positive-framing shares | three-level mixed |
| KQXUE | GDP growth and unemployment | cosmopolitan + utilitarian shares | country FE |
| C6HJR | GDP growth and unemployment | positive framing, 7-item share | two-way FE |
evidence for the claim
Free-phase records: the public Multi100 dataset (osf.io/q5h2c), which contains only categorical verdicts and free-text reports. The models themselves were read from the analysts’ public code.
The disagreement among our five analysts follows a pattern that was described first, and at far greater scale, in two studies on which the rest of this part rests.
Silberzahn and colleagues (2018) – the red-card study from earlier, in which twenty-nine teams analysed one dataset to answer one question. Twenty of the twenty-nine intervals excluded 1, and nine did not.

Auspurg and Brüderl (2021) went back through all twenty-nine of those analyses and asked a question that the original had not: what was each team actually trying to measure?
The first establishes that careful people disagree, and the second explains why.
Auspurg & Brüderl re-read the 29 analyses and found four different estimands:
descriptive association · discrimination net of mediators · variance-maximising · exploratory
The dispersion came largely from a multiverse of questions, rather than from a multiverse of analyses.
Auspurg and Brüderl (2021)
Auspurg and Brüderl (2021). Top row: the 29 published estimates. Bottom row: drawn to their reported median (1.28) and SD (0.06) – the 486 values are not published.
Auspurg and Brüderl put the question in their title: has the credibility of the social sciences been credibly destroyed?
If the twenty-nine teams answered four different questions, most of their disagreement is not evidence that analysis is unreliable.
It is evidence that the question was under-specified – an ordinary, fixable defect.
Read this way, the many-analyst literature is a diagnosis of vague research questions rather than a verdict on social science.
This reading is more optimistic, and also more demanding, because it places the problem with whoever writes the question.
Auspurg and Brüderl (2021)
Lundberg et al. (2021).
X = predictor, Y = outcome. Green = the causal effect you want; coloured = the path you must reason about.

The same slope fits four different stories:
The data look identical in all four. Only an assumption about how the data arose can tell them apart, and a DAG is where you write that assumption down.
Association flows along every open path, not only along the causal arrow. A fork or a chain is open until you adjust for the variable in its middle. A collider is the reverse: shut by default, and opened when you adjust for it.

Control for everythingcan switch you to an estimand you never chose
The expected change in [unit quantity] among [target population] for a 1-percentage-point difference in unemployment, holding [assumptions] fixed.
In chat, now: your one-sentence estimand for the claim. Type it, hold it, send on go
.
Observational panel · 27 countries · no assignment mechanism · feedback between the variables · a decade of shared shocks.
Nothing in this design licenses unemployment causes a shift in EU framing
.
So why draw a causal graph at all?
Because the graph still does three jobs that do not need identification:
The graph and the estimand are only worth anything if they are published where someone else can read them:
In Task 5 you enter all of that in a block of the report in your workspace – the estimand, the graph-based justification, the one specification, and the rule for what would count as support – and you do it before you run the model.
The technical name for that is preregistration, and it fixes the question before anyone knows the result.

The same graph in R. Every arrow is a formula, read outcome-first – so a # takes an arrow out and the code still runs:
adjustmentSets()With four pieces of syntax you can read most R: assign with <- · call a function with name(...) · reach into an object with $ · chain with the pipe |>.
What comes back is a data frame: a rectangle, one row per observation, one column per variable, each column of a single type. Almost everything in R takes a data frame and gives you one back.
library() makes the functions in a package available. It does not install it – that is install.packages(), and it happens once, not every time.
Every serious error I have made in twenty years of this was visible in the data before it reached a model – a duplicated year, a missing value coded as -9, a scale running 1–10 where I assumed 0–1.
Read it downwards as a sentence: take the panel, then keep 2008 onwards, then keep four columns, then add a centred unemployment column, then sort by unemployment, highest first.
The sixth verb is summarise(), usually with group_by(): collapse many rows into one per group. That is how the panel you are working on was built from 390,000 respondents.
The tilde ~ reads outcome on the left, predictors on the right. Add predictors with +. The formula is an object in its own right and is not tied to the function that will eventually use it.
Almost every modelling function in R takes this same shape: something(formula, data = ...). Learn the pattern once and lm, glm, lmer, feols and betareg all become variations.
The bar | separates the regression from the fixed effects. Everything after it is absorbed: cntry gives every country its own intercept, year gives every year its own.
The equivalent with dummies would be mcosmo ~ unemp_c + factor(cntry) + factor(year) – same estimate, 36 extra rows of output you do not want to read.
This is the model that you reproduce in Hands-on 3, where it gives t = −3.804.
broom::tidy() returns the table as a data frame, so you can filter it, join it, plot it. That is how 840 model fits become one chart when we reach the multiverse.
Three parts, always: the data, the mapping (aes) from variables to visual properties, and one or more geoms that draw something. Layers stack with +.
facet_wrap repeats the whole plot once per group. One line of code gives twenty-seven small country panels, and you can see which countries move.
Each line is one arrow, read outcome-first, following the same convention as a model formula. Because every arrow has its own line, you can comment one out with a # and the code still runs.
adjustmentSets() returns the variables that close every back-door path in the graph. Change an arrow, rerun, and watch the answer change.
| What you see | What it usually means |
|---|---|
could not find function "feols" |
The package is not loaded – add library(fixest) above |
object 'euframes' not found |
You ran this chunk before the one that creates it – go back up and run in order |
object 'unemp_c' not found |
The column does not exist yet – you skipped the mutate() that makes it |
+ on the console, nothing happens |
An unclosed bracket or quote. Press Esc and look for the missing ) |
Read the error. R error messages are terse but almost always literally true, and most of them name the thing that R could not find.
Task 2 has three pieces:
# off the arrows you are willing to defend, rerun adjustmentSets(), and check which adjustment set it now returnsTwo sentences for your notes: what do two-way fixed effects adjust for, and what can they not fix?
Whatever you decide here becomes your starting point on the menu in Task 4, and the justification you preregister in Task 5.
An hour – the return time is in the chat · lunchtime browsing, if you like: the specification menu
Part 3 · 90 minutes
Focus on the
Cosmopolitan
dimension for EU framing and on the unemployment rate as the independent variable; disregard contextual variables.
Three instructions. Everything else – estimator, sample, weighting, functional form, how you build the scale – was left to us.
Multi100 was designed to fix just enough to make the numbers comparable and to leave the rest free, so that what remained would be a measurement of analytical variability itself.
The cosmopolitan dimensionassumes that the five items form one scale, and that the summary used in the paper, each respondent’s share of mentions, is the right one. A mean, a sum, or a factor score would all be defensible
The unemployment rateassumes that the claim is about unemployment. The abstract of the paper says
poor economic performances– which also names GDP growth
Disregard contextual variablesremoves the country-level controls. That makes results comparable across analysts and gives up any defence against a time-varying confounder
None of these is wrong. Each one is a decision, made for you, that a different study could reasonably have made differently.
EUframes_cy.csv – 270 rows, one per country-year · 27 countries · 2004–2013
| Column | What it is |
|---|---|
cntry, year |
the cell |
mcosmo |
mean cosmopolitan framing in that country-year, 0–1 |
mutil, mcomm, mlib |
the other three framing scales |
mpos, mneg |
the two composites – all positive items, all negative items |
unemp, growth |
national unemployment rate and GDP growth |
n_cy |
how many survey respondents are behind the cell |
bailout |
1 if an EU/IMF assistance programme was running |
n_cy is the interesting one: cells are built from very different numbers of people, which is why weighting is a live choice on the menu.
What Multi100 asked: focus on the cosmopolitan
dimension and on the unemployment rate; disregard the contextual variables
What I submitted: a two-way fixed-effects model, mcosmo ~ unemp_c | cntry + year
What Multi100 recorded: t ≈ −3.80 (df = 233, N = 270, partial r ≈ −0.242)
mcosmo, unemployment centred, country and year fixed effectsEUframes_cy.csv, loaded as the object euframesTarget: t = −3.804 – the value recorded for analyst C6HJR in the Multi100 dataset. When you have it, type your t in chat and hold it until I say go
.
Thirty of you just got t = −3.804, to the third decimal. That is reproducibility, and it is the easy R.
You all ran the same code. The five Multi100 analysts each wrote their own and came back with five different answers.
None of them was careless. They made different choices, and the options that they chose from make up the menu.
| Axis | Options |
|---|---|
| Outcome | mcosmo · mutil · mcomm · mlib · mpos · mneg |
| Outcome family | gaussian · beta (logit link, for a bounded 0–1 scale) |
| Predictor | unemployment · GDP growth |
| Predictor form | raw · log (unemployment only – growth goes negative) |
| Copredictor | none · the other macro measure |
| Estimator | two-way FE · country FE · random effects · pooled OLS |
| Sample | all · 2004–2008 · 2009–2013 · no bailout countries · no Greece and Spain |
| Weights | none · n_cell · n_eff · pop |
Eight of the fourteen axes. The other six – scoring rule, no-opinion handling, item set, cell weighting, published series and standard errors – are on the specification menu page, with copy-paste code for every option.
Two of these axes already varied in the red-card study, where the 29 teams split across four response distributions (outcome family) and four ways of handling non-independence (estimator).
The three instructions fixed the outcome dimension, the predictor and the contextual controls. Everything else stayed open – including one thing that few people think of as a choice:
The unit of analysis. Two of the five fitted models on individual respondents; three worked on aggregates. Task 2 never said which, and the wording did not imply one.
Their five answers all obeyed the three instructions and still ran from r = −0.006 to r = −0.45, so the freedom that remained was enough to produce the whole spread.
The choices that move an estimate most are usually made before any model is fitted.
In Hands-on 4 the rule is: change one axis, hold the other thirteen at baseline.
Baseline = the specification you reproduced in Hands-on 3: mcosmo · gaussian · unemployment · raw · no copredictor · two-way FE · all years · unweighted · share of mentions · standard measurement settings · conventional SEs.
Changing one axis at a time keeps the class chart readable. Every dot differs from a common baseline on exactly one axis, so when a dot moves you can say what moved it.
And one convention binds everyone: claim-alignment. The negative framings are reverse-coded, 1 − y, so that supports the claim
reads as a negative sign whatever your outcome. Without it the class chart would straddle zero by construction and look like noise.
| Scale | Items | Claim predicts |
|---|---|---|
mcosmo |
peace · democracy · free movement · diversity · voice | negative |
mutil |
prosperity · social protection | negative |
mcomm |
unemployment · identity · crime · borders | positive |
mlib |
bureaucracy · waste of money | positive |
mpos |
all seven positive items | negative |
mneg |
all six negative items | positive |
The claim says decrease positive dimensions
– mpos is the more literal reading, mcosmo is what was instructed. Across all six scales, the choice of outcome accounts for roughly half the variance in the grid as estimated, most of it because positive and negative framings take opposite signs. The disagreement between mpos and mcosmo accounts on its own for 0.15%.
Five binary items per respondent. Three defensible summaries:
A share is relative to the respondent’s own total, so somebody who ticked eight boxes contributes differently from somebody who ticked two. The mean and the sum do not depend on that total.
Among the five Multi100 analysts, one used a mean, one a sum, one a share and one three separate shares. Each choice is defensible, but no two of them measure the same quantity.
Reverse-coding a bounded scale flips the sign of b, t and r, and leaves the magnitude, the standard error, the |t| and the p-value untouched. The same holds exactly for the beta family, since logit(1 − μ) = −logit(μ).
Your evidence stays the same, and only the sign convention in which it is reported changes.
It is still an analytic decision, so it is set by an explicit claim_align switch in your preregistration, where a reader can find it.
Our outcome is a proportion. It lies strictly between 0 and 1, and its variance is smallest near the ends. A linear model assumes neither of those things.
Fitted as a beta regression, the baseline gives b(logit) = −0.01399, z = −4.075, r = −0.258, against r = −0.242 from the gaussian fit, so the association keeps its direction and its significance and is marginally stronger.
The choice of distribution matters in principle, and on this claim it changes the result very little.
The abstract says poor economic performances
. That names at least two things:
Refitted on our panel, the free-phase model of analyst C6HJR (mpos ~ growth + unemp, two-way fixed effects) puts the growth coefficient at b = +0.00167, t = 1.59, in the direction predicted by the claim but not significant.
Across all the growth specifications in the grid, 70.5% point in the direction predicted by the claim, but only 46.9% are significant. Direction without significance is a different kind of support from the one that the unemployment specifications give.
unemp – a one-point rise in the rateThose two are genuinely different models and can give different answers.
But centring and standardising are not. Subtracting a mean, or dividing by a standard deviation, is an affine change of scale: it moves the coefficient and its standard error by the same factor and leaves t, r and p exactly unchanged.
For that reason the grid has no separate centred rows. A robustness check that varies only the centring is the same analysis restated.
| Estimator | What it compares | What it assumes |
|---|---|---|
| Pooled, clustered SE | countries with each other and over time | no unobserved country differences |
| Country FE | each country with itself over time | country differences are stable |
| Two-way FE | the same, net of EU-wide year shocks | shocks hit everyone alike |
| Random effects | a weighted blend of both comparisons | country effects uncorrelated with the predictor |
The four estimators address different questions. A pooled estimate is about whether high-unemployment countries frame the EU differently, a fixed-effects estimate about whether a country frames it differently when its own rate rises.
Of the 41 cosmopolitan specifications with the wrong sign, opposite to the one that the claim predicts, 33 are pooled.
Absorbed
Not addressed
This is the same statement as the DAG from Hands-on 2. adjustmentSets() on its solid part, before any of the pale nodes is wired in, returns country and year, which are what two-way fixed effects absorb, so the graph and the baseline model make the same claim.
Put GDP growth in beside unemployment and you have made a choice you should be able to name:
poor economic performance. Now you are asking which of the two the claim is about
The line of code is the same in all three cases, but each reading of it targets a different estimand. And remember the Table 2 fallacy, which is to read every coefficient in a regression table as a causal effect. The growth coefficient in that model is not automatically the causal effect of growth.
The last two exclusions test whether the association depends on a handful of extreme cases, which would be a reason for concern. In this grid, 24 of the 41 wrong-sign specifications come from samples that leave out the bailout countries.
An exclusion is a claim about who the finding is meant to be about, and so it is a question about the estimand.
Each cell is a mean of very different numbers of respondents. Weighting by n_cy makes a cell built from 2,000 people count for more than one built from 500.
n_cy – cells count in proportion to the people behind them. You are closer to studying peopleThat changes the estimand, which is why the weight is one of the fourteen axes of the menu, beside the outcome and the estimator.
Practical note: random effects are never fitted with weights in the grid – the combination is not well defined for this design, so those rows do not exist.
A coefficient from a beta model on a share and one from a weighted pooled model on a sum are not on the same scale. Multi100 and this course put them on one scale in three steps:
Every specification in the class, and all 840 in the grid, can then be placed on one axis. submit_exploration() in Task 4 and report_result() in Task 5 do the conversion for you.
Standardising makes numbers comparable, not equivalent. Two analyses that estimate different quantities remain different after the conversion, even when they are plotted side by side.
Silberzahn’s teams faced the same problem: four of the twenty-nine reported a correlation or a standardised difference rather than an odds ratio. The authors converted everything to one scale and reported the median. A median summarises the estimates in one number, where a curve shows their whole distribution, and which of the two you want depends on what you mean to say next.
Task 4 is exploratory. Try several specifications, look at what each one does, and get a feel for which axes move the estimate.
try_spec() and read what happens to b, t and the partial r; my_experiments() lists every trytry_spec(), so that it is your last try, and submit it with submit_exploration()Your dot appears on the live Multiverse page within seconds, and we read the dots from the whole class once everyone has submitted.
This is not your preregistered analysis. You are allowed to look, change your mind and try again, which is why this phase has a name of its own.
30 minutes – the return time is in the chat · leave the Multiverse page open
Part 4 · 90 minutes
You ran three or four specifications, all defensible, and watched the estimate move.
Nothing about that is unusual. Every analysis you have ever published came from a space like that – you reported one point from it.
The multiverse idea is the proposal that the space is itself a finding, and that showing one point without it is a form of incomplete reporting.
Under all these names the proposal is the same: stop treating your analytical choices as invisible, and enumerate them.
Four binary choices give sixteen analyses. Fourteen axes with several options each give hundreds of thousands. The published paper reports one red line and no tree.
Two filters decide which of the analyses you could run go into a multiverse:
Drop the first filter and you get a cloud of nonsense that makes any finding look fragile. Drop the second and you are averaging over answers to different questions – which is the red-card mistake, at scale.
Our grid is a curated 840, chosen against both filters. That curation is itself an analytical decision, and it is the one that a critic should attack first.
Enumerate
Cross every option on every axis. Complete, mechanical, reproducible.
Risk: the set fills up with specifications nobody would defend, and the spread stops meaning anything.
Curate
Include only the specifications you would actually defend.
Risk: you are choosing the set, so a sceptic can ask whether you chose it to get the picture you wanted.
Either way, the set is a claim, and it belongs in the paper alongside the curve, ideally preregistered before the curve is drawn.
Our menu, multiplied out:
Two consequences. Compute stops being free, so somebody must decide what is worth running. And you cannot look at 450,000 numbers – you need a picture, and a way to ask which axis is doing the work.
Rohrer, Hullman and Gelman’s question, and it has three different answers that need three differently built universes:
Our grid was built for reflection. Reading it as inference is a mistake that we take apart later.
Rohrer et al. (2026)
Once you have hundreds of estimates and the choices that produced each one, you can ask a question that no single analysis can answer: how much of the variation does each axis account for?
The answer is the most useful thing a multiverse produces, because it tells you where to spend your argument. An axis that moves nothing does not need defending, while an axis that moves everything needs a paragraph.
On this claim, the answer surprises almost everybody who guesses it in advance.
Five questions to ask any curve you meet, in any paper:
Answered for your own curve, the five make a publishable robustness section.
Pre-computed over the specification menu (data/spec_grid_full.csv), claim-aligned – negative framings reverse-coded and growth predictors sign-flipped, the convention that the preregistration block in Task 5 records (claim_align = TRUE).
Your own dots are on the live Multiverse page, drawn on this same chart
submit_exploration() in Task 4 and report_result() in Task 5 each give you a one-click link to the Google Form; results appear on the Multiverse page.
Six axes: outcome · predictor form · estimator · copredictor · sample · weights
Which axis moves the estimates most across all 840 specifications?
Type your guess – hold it – send on go
.
Computed over the committed data/spec_grid.csv; imported as data/fork_importance.csv.
try_spec() with the choices you submitted in Task 4: its output includes your placement. Everyone uses the lab for this step, because the workspace does not print itPaste your percentile in chat – hold – send on go
.
A curve is a map of the menu you wrote, and it cannot show whether that menu was the right one.
Description – map how the estimate moves across every defensible choice. (What our curve does.)
Inference – ask whether the curve as a whole is surprising if there were no effect at all. (A different, harder job.)
The two are detachable, and most published curves stop at description.
Every specification re-uses the same 270 country-years. The estimates are deeply dependent.
63.9% significant
describes the curve. It is not a p-value for the claim.
Vote-counting across dependent specifications has no null distribution, so a tally of significant results is not evidence for the claim on its own.
The design is worked through in your inference reading; permutation inference after Phipson and Smyth (2010).
Discussion (2 minutes, then chat): the constrained cosmopolitan-only universe has 140 specs: 70.7% negative, 57.1% negative and significant. Strong evidence? Against what null?
Everything descriptive survives: the 49.7% outcome fork, the analysts’ placement, the sign-flip pattern, your dot.
Only one sentence depends on the permutation test: the curve as a whole is unlikely under no effect
.
Careful reporting says which of the two layers a claim belongs to.
Robustness is better assessed with a few thoughtful models than with billions of regressions(Auspurg 2025; answering Ganslmeier and Vlandas 2025) – only justified models targeting the same estimand belong in one curve. Our grid is a curated 840, and a decomposition by fork does the job that every critic endorses: it locates where the argument must happen
There is only one correct analysisonce the question is fully specified (Lakens et al. 2026) – your preregistered specification guards against cherry-picking, and their deeper point is that preregistering a choice does not yet make it justified, which is what the estimand and DAG work were for
What’s a multiverse good for anyway?– reflection, persuasion, or inference, and each purpose needs a differently built universe (Rohrer et al. 2026). Ours was built for reflection, and how much more to read into it is the open debate
The argument from Lakens and colleagues runs in four steps:
Take it back to the red cards. Twenty-nine teams modelled one count of red cards as linear, logistic or Poisson. Those are claims about how the outcome was generated, and they cannot all be right – so on this argument most of the spread is error.
Lakens et al. (2026)
Grant the argument, and suppose that one correct analysis exists. Which one is it?
Getting there needs the estimand and the identification assumptions and agreement about the measurement – and on this claim, reasonable people disagree about all three.
So the curve complements the one-correct-analysis view: it is a map of the disagreement that remains, drawn because we cannot yet settle it.
And it points at what to argue about: on this claim, at the outcome, not at the estimator.
It does settle
It does not settle
A badly reasoned analysis that is preregistered is still badly reasoned; the preregistration only adds a timestamp
That is why the estimand and the DAG came first today: preregistration protects a choice, and the causal reasoning is what justifies it.
In Task 4 you explored: several specifications, tried in order to find out what they do. That was the right way to learn them
In Task 5 you preregister one specification: it goes into the preregistration block of your report before it runs, with a reason that your DAG supports
The code is identical. The difference is that one of these can be chosen because of its result and the other cannot
declaredin chat – then wait for the class
Nobody runs a model until everyone in the class has completed their preregistration block.
report_result() prints a one-click link to the Google Form with your results already in it – click, submitCompare the two waves. If the preregistered dots fall systematically to one side of the exploratory ones, it is worth discussing why.
Stuck? Chat STUCK
· reload and rerun · second browser · follow my screen · last resort: the grid lookup on the menu page gives you the results for your specification by hand
the common single-path analyses in social and behavioural research should not be simply assumed to be robust to alternative analyses
(Aczel et al. 2026)
A blueprint: precise question → causal reasoning (DAG) → multiverse → sensitivity analysis (Auspurg and Brüderl 2021)
There is no privileged single analytical path – only justifiable ones, explored openly.
You made one justifiable choice today, preregistered it with its reason before running it, and placed it inside a multiverse.
codemoreh.github.io/applied-replication
Everything is open, and you can fork any of it.
fixest & ggdag docsFeedback form
(link in the chat · two minutes)
