
One day – social data science tools
Chris Moreh
Applied replication for data skills · a CodeMoreh workshop
A full-day workshop · four parts · the tools in the foreground
You leave with: a working reproducible workspace · a reproduced result · a rendered report with a preregistration block · a map of the toolchain, with your own practice placed on it
Work along in your IDE, solo or in pairs, with the browser lab page as the fallback
What I want you to understand
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)
Sánchez-Tójar et al. (2026)
Miske et al. (2026).
Miske et al. (2026). Published counts: the 143 assessed papers come from 182 datasets, 38 of them rebuilt from source data.
The most substantial barrier … was the unavailability of author data.
Reproducibility success does not mean that the finding is correct.
Miske et al. (2026).
Aczel et al. (2026).
Aczel et al. (2026).
Tyner et al. (2026).
Tyner et al. (2026).
Reproducible ≠ robust ≠ replicable.
Across SCORE the three are essentially uncorrelated – knowing one barely predicts the others.
So there is no single number for trust
. You have to ask which repetition you mean.
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.
lm(), of feols(), of every package that this course loads, is public, so no estimator has to be taken on trustWith a proprietary toolchain, the price of reproducing your work falls on your reader, who has to buy the licence.
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
Pythongeneral purpose, good for machine learning
Juliafast numerics
Positronthe course default
VS Codeeverything, via extensions
Jupyternotebooks, Python-first
Quartotext + code → anything

Ringed in orange: the tools that this course uses. The others are there so that you know what you are choosing between.
PythonA general language that also does data. Better for pipelines, scraping, text and machine learning.
JuliaFast, young, small ecosystem. Worth it when your model takes hours.
You do not have to choose once and forever. Quarto runs R, Python and Julia chunks in the same document, and the course browser lab is R compiled to run inside a web page.
PositronThe newer IDE from Posit. R and Python as equals, VS Code underneath.
VS CodeDoes anything, once configured. Big extension ecosystem.
JupyterNotebook-first. Beware: cells can run out of order.
NeovimFor the keyboard-only. Steep to learn, and very fast once learned.
Of the choices in the toolchain, the editor matters least. It changes how you work and nothing about what a reader receives.
A trap: a notebook lets you run cells out of order, so it can show output that no clean run would produce. Restart and run everything before you believe it.
Positron and RStudio are both from Posit; both are free, and RStudio is open source.
.RCode only, with prose in comments.
.mdProse only. Headings, lists, links – no code.
.RmdProse + R, rendered together. R only.
.qmdProse + R, Python or Julia. The course format.
Every one of these is a plain text file. You can open it in anything, compare two versions of it line by line, email it, and read it in twenty years. A .docx with pasted output is not plain text, and nothing in it records which numbers came from which code.
You write one source file. Rendering runs your code, inserts the results, and produces whatever output you asked for from that one source.
tidyverseReading, reshaping, plotting. One grammar across all of it.
easystatsModel output as tidy data: parameters, performance, effect sizes.
modelsummaryRegression tables you can publish, from the fitted objects.
marginaleffectsPredictions, contrasts and marginal effects from almost any model.
The tasks also use fixest (fast fixed effects – the estimator behind our baseline), dagitty and ggdag (causal graphs). The full setup is on the setup page, and the check script in your workspace reports whether all three are installed.
Git is a program on your machine. It records a complete, dated snapshot every time you say so, and it never forgets one.
GitHub is a website that hosts those snapshots so other people can reach them – and it is not the only one (GitLab, Codeberg, one hosted by your university).
What it gives you that a folder of final_v3_REALLY_final.R cannot:
The tasks use none of it. There is a companion module on the site, and your workspace folder is the ideal thing to practise on.
OSFProjects, files, and registrations. Free, and it hosts the public record of our case.
A repository is not an archive. A GitHub repository can have its history rewritten, be renamed, or be deleted tomorrow. An archived deposit gets a DOI, a fixed version, and a commitment to keep serving it. Serious work usually needs both.
| What | Common choice | Why |
|---|---|---|
| Code | MIT · GPL | MIT: use it however you like. GPL: and share your changes back |
| Data | CC0 · CC BY | CC0 puts it in the public domain; CC BY asks for attribution |
| Text, figures, slides | CC BY · CC BY-NC | Attribution, optionally no commercial reuse |
| Someone else’s data | their terms | Eurobarometer is GESIS-licensed – I may not redistribute it, and neither may you |
Publishing with no licence is not a neutral default. It means all rights reserved – legally, nobody may reuse the work at all.
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.
An hour – the return time is in the chat · lunchtime browsing, if you like: the exercise overview
Part 3 · 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.
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()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.
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.
30 minutes – the return time is in the chat · leave the Multiverse page open
Part 4 · 90 minutes
| 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.
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.
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.
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.
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
OSF, the Open Science Framework: a public repository in which data, code and registrations are timestamped and citable.
A registration is a frozen, dated snapshot of a plan. Anyone can check what you said you would do, and when.
You need no account today. We are going to read OSF the way that reviewers do.
osf.io/8rtwe – the archival Multi100 record: analyst C6HJR, frozen as submitted
osf.io/6zqct – the maintained fork: the script, the codebook, the course data
osf.io/h7432 – the full SCORE dossier on the EU-frames case: all the reports and their verdicts
Aczel et al. (2026). Project: osf.io/7snkz
Preregistration Template from AsPredicted.org)
| AsPredicted asks | Your block answers |
|---|---|
| Q1 Have data been collected? | It’s complicated – I already hold them+ what I already know |
| Q2 Main question / hypothesis? | the claim, in your one sentence |
| Q3 Dependent variable? | your outcome axis |
| Q4 Conditions? | your choices on the other twelve axes |
| Q5 Analyses? | your model + your support/undermine rule |
| Q6 Outliers / exclusions? | your sample axis |
| Q7 Sample size? | N = 270 country-years, fewer if your sample or series leaves some out |
| Q8 Anything else? | your DAG-grounded justification |
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)
