Science Journaling Club Founded 2024

INTERACTIVE COMPANION · METHODS · STATISTICS

The Paradox Constructor

A live model accompanying “Building a Dataset Where Every Group Trends Up and the Whole Trends Down”

← Read the full article

Two machines below. The first lets you build Simpson's paradox with your hands: drag five groups around a plot and watch the line fitted to all of them disagree with every one of them. The second measures how often that disagreement turns up on its own, by simulating datasets in your browser and counting.

Both run the same arithmetic as analysis/simpsons-paradox.py, and the page opens on the settings that produce the article's headline numbers.

Model 1. Build one by hand

Five groups sit on the plot. Each one holds points arranged on a line of slope \(b_w\), which starts at \(+1\), and each group's filled marker is its mean. Drag a marker and the group moves with it. The single line fitted to all the points is drawn heavy and dark.

The identity underneath is the one the article turns on. Write \(T_w\) for the spread of \(x\) inside groups, \(T_b\) for the spread of the group means, and \(w = T_w/(T_w+T_b)\). Then

$$b_{\text{pooled}} = w\,b_w + (1-w)\,b_b,$$

with \(b_b\) the slope through the group means weighted by group size. The readout prints both the fitted pooled slope and the value this identity predicts, so you can watch them stay equal to the last decimal while everything else moves.

pooled slope: identity says: w: b_between: N:

Model 2. Count how often it happens by accident

Now stop building and start counting. Each trial generates a fresh dataset: \(G\) groups totalling 240 points, every group carrying a true slope of \(+1\), residual standard deviation 1. A group-level lurking variable \(z_g\) is drawn standard normal, shifts each group's mean in \(x\) by \(c z_g\) and its level in \(y\) by \(c\theta z_g\), and the loading \(\theta\) is redrawn from \(N(0,\tau^2)\) every trial. So the confounder pushes with the real effect half the time and against it half the time, which is what “by accident” has to mean.

A trial counts as a reversal when every within-group slope has one sign and the pooled slope has the other. The gated count is stricter: every one of those slopes must also clear a two-sided \(t\) test at \(\alpha = 0.05\), so it is the rate at which a careful analyst would end up holding two confident findings pointing opposite ways.

trials: 0 reversal rate: SE: gated rate: mean w:

Three things to check by hand

A model you cannot check is a cartoon. Three of the numbers on this page can be verified with arithmetic, and if any of them disagrees with what you see, the page is broken and we would like to know.

Model 1, opening settings
pooled slope −1.000000, within-group slopes +1.000000
the sum behind it
T_w = 85.714286, T_b = 600, w = 1/8, b_b = −9/7
and so
(1/8)(+1) + (7/8)(−9/7) = 0.125 − 1.125 = −1 exactly
Model 1, group 1 mean
y = −(9/7)(−4) = 36/7 = 5.142857
Model 1, stacked preset
T_b = 0, w = 1, so the pooled slope equals the size-weighted average of the within-group slopes exactly
Model 2 at c = 0
reversal rate exactly 0, however long you run it
Model 2 at the defaults
converges on 0.09695 ± 0.00209, the article's 20,000-trial value
Model 2 at 30:1 sizes
converges on 0.04855, about half the balanced rate

The last two are the only ones with sampling error in them. This page runs fewer trials than the article does, so expect the estimate to wander by a few thousandths before it settles. The band drawn around the running curve is one standard error and the dashed line is the article's answer, which carries a standard error of its own. The readout compares the two on the standard error of their difference rather than on this page's alone, because comparing an estimate to another estimate as though the second were exact is a way of manufacturing disagreements. We ran 40,000 trials of this code outside the browser and got 0.09405, which is 1.14 such standard errors from the article's 0.09695. Past three would mean one of the two implementations has a bug in it.

What this page cannot show you

It cannot tell you which slope is the right one to report. Nothing can, from the data alone. The paradox is a fact about weighted averages and carries no information about what caused what, so deciding between the within-group answer and the pooled answer needs a causal model that no amount of dragging will supply.

It also flatters the world in three specific ways. Every group here shares one slope, which real groups never do, and unequal slopes make reversals easier rather than harder. The errors are Gaussian, and heavier tails would raise every frequency on the second panel. And the groups are labelled: you can see them. The case that should actually worry a reader is the one where the confounder was never recorded, where the arithmetic is identical and the plot is a single undifferentiated cloud with one line through it.