Causal diagrams: nodes and arrows
In the previous guide we met a hard truth: two things can move together for reasons that have nothing to do with one causing the other. Correlation is not causation. But that leaves a practical question unanswered — when we DO see an association in our data, how do we judge whether it is causal, and what we would have to do to measure the real effect? The single most useful tool for this is not a formula. It is a picture.
That picture is called a causal DAG — short for directed acyclic graph. Do not let the name scare you. It is just dots and arrows. Each dot, called a node, is a variable you care about — say, a person's coffee drinking, their risk of heart disease, or whether they smoke. Each arrow points from a cause to a thing it directly affects: if we believe smoking directly raises heart-disease risk, we draw an arrow from smoking to heart disease.
Two words in the name carry real meaning. Directed means every arrow has a direction — “A causes B” is not the same as “B causes A.” Acyclic means you can never follow the arrows in a loop back to where you started: a variable cannot, even indirectly, cause itself. Together these rules force you to write down a clear story of what affects what, rather than a vague tangle.
One more word and we can start. A path is any chain of arrows connecting two variables when you ignore the arrows' directions for a moment. Some paths are the real causal route from cause to effect — coffee → … → heart disease. Other paths are sneaky detours that create an association without any causation flowing along them. The whole game of this guide is learning to tell those two kinds of path apart. And one phrase will keep coming up: to control for a variable (also called adjusting for it, or conditioning on it) means to compare like with like — for example, to look at the coffee–heart-disease link separately within smokers and within non-smokers, instead of all mixed together.
Confounders: the common cause
Here is a real example that fooled people for years. Studies found that heavy coffee drinkers had more heart disease. Does coffee damage your heart? Look at the data alone and you would say yes. But a third variable is lurking: smoking. People who smoke tend to drink more coffee, and smoking independently raises heart-disease risk. Smoking is a common cause of both the thing we are studying (coffee) and the outcome (heart disease).
A variable like smoking is called a confounder. Its signature on a causal diagram is two arrows pointing OUT of it: one into the treatment and one into the outcome. That shape — treatment ← confounder → outcome — is a fork, and it opens a backdoor path between coffee and heart disease that carries a fake, non-causal association. We measure the association, then wrongly read it as cause.
A three-node diagram. An arrow goes from Smoking to Coffee, another from Smoking to Heart disease, and a separate arrow from Coffee to Heart disease. Smoking sits above as the common cause.
How do we get the real effect? We compare like with like. Split the data by the confounder: look at coffee versus heart disease among smokers only, and again among non-smokers only. Within each group smoking is now held constant, so it can no longer create a spurious link. If the coffee–heart-disease association shrinks to nothing inside both groups, the original association was confounding, not cause. This splitting-and-comparing is exactly what statisticians mean by controlling for, adjusting for, or conditioning on the confounder.
It helps to picture what we actually measure as a sum of two parts.
Every confounded comparison is the real effect plus a bias term from the open backdoor path. Controlling for the confounder closes that path and zeroes out the bias — IF you have measured and adjusted for the right variables.
Colliders: why controlling can hurt
By now you might be tempted by a simple rule: when in doubt, control for everything. Throw every variable into the regression and let the math sort it out. This is one of the most common and damaging mistakes in data analysis, and the reason is a creature called a collider.
A collider is the mirror image of a confounder. Instead of two arrows pointing out, it has two arrows pointing IN — it is a common effect of two variables: cause A → collider ← cause B. The two causes “collide” at it. Here is the surprising part. Along a path through a collider there is naturally NO association — the path is closed by default. But the moment you control for the collider, you OPEN the path and manufacture an association that was never there.
An example makes it click. Imagine that, in the general population, an actor's acting talent and their good looks are completely unrelated — knowing one tells you nothing about the other. Now look only at actors who got cast in a hit movie. To get cast you usually need to be either very talented or very good-looking (or both). “Got cast” is a collider: talent → cast ← looks. Among the cast, the two traits are now negatively related — the most gorgeous stars tend to be the weaker actors, and the most brilliant actors tend to be more ordinary-looking. Not because beauty erodes talent, but because we filtered on their common effect.
A diagram with Talent and Looks each pointing an arrow into a third node, Got cast, which is drawn boxed to show it is being conditioned on. There is no arrow directly between Talent and Looks.
This is not an exotic edge case. Whenever you analyze only a filtered slice of the world — only customers who bought, only patients sick enough to be hospitalized, only users who survived to week two — you are conditioning on a collider, because being in the slice is a common effect of many things. That is exactly the mechanism behind selection bias. The practical lesson is blunt: do not control for a variable just because you can. Control closes forks (good — it kills confounding) but opens colliders (bad — it creates bias). You need to know which is which, and for that you need the diagram.
Mediators: don't block the path you're studying
There is a third role a variable can play, and it trips up even careful analysts. A mediator sits ON the causal path, between cause and effect: treatment → mediator → outcome. It is one of the ways the cause produces the effect — it carries part of the causal story, rather than faking it.
Suppose more education really does raise income. One big reason is that education helps you land a higher-paying kind of job: education → job type → income. Job type is a mediator. Now ask what happens if you “control for job type” — if you compare the income of more- and less-educated people who hold the same kind of job. You have just blocked the main route by which education pays off. The leftover effect (education's direct effect, holding job fixed) will look small, and you might wrongly conclude that education barely matters for income.
So the same statistical action — controlling for a variable — is right, wrong, or harmful depending entirely on the variable's ROLE in the causal story: control a confounder (a common cause) to remove bias; leave a mediator (a step on the path) alone to keep the effect; and never control a collider (a common effect), or you invent bias. The variable's name and its numbers look identical in all three cases. Only the diagram tells them apart.
The backdoor criterion in plain words
We can now state the rule that ties everything together. It is called the backdoor criterion, and it answers the central question: which variables must I control for to read the true causal effect of treatment X on outcome Y?
First, the name. A backdoor path is any path from X to Y that leaves X through an arrow pointing INTO X — that is, a path of the form X ← … — Y. These are the non-causal side routes (every confounder opens one). The real causal effect flows only out the “front door”: X → … → Y. Our job is to block every backdoor path while leaving the front door wide open.
Whether a path is open or blocked follows three small rules about its middle nodes:
- A chain (A → B → C) and a fork (A ← B → C) are OPEN by default. Controlling for the middle variable B blocks them. (Forks are confounding — this is the case where controlling helps.)
- A collider (A → B ← C) is BLOCKED by default. Controlling for the middle variable B — or for anything B causes — OPENS it. (This is the case where controlling hurts.)
- A path is blocked if ANY single node along it is blocked. So to neutralize a whole path, you only need to block it at one point.
Now the backdoor criterion itself, in two clauses. A set of variables Z lets you identify the causal effect of X on Y if: (1) controlling for Z blocks every backdoor path from X to Y; and (2) Z contains no descendants of X — nothing that X causes, which rules out mediators and any colliders downstream of the treatment. Find such a Z, adjust for it, and the association you measure becomes the causal effect.
When Z satisfies the criterion, the recipe for combining the within-group effects has a name — the adjustment formula. In words: compute the effect inside each level of Z, then average those effects, weighting each by how common that level is in the whole population.
The backdoor adjustment formula. The do(X=x) on the left means “if we had intervened and set X to x” — the causal quantity — as opposed to merely observing X=x. The key is the weight Pr(Z=z): the whole population's mix, not each treatment group's own mix. Using each group's own mix is exactly the mistake behind Simpson's paradox, next.
Simpson's paradox, explained by a DAG
Everything so far comes to a head in a single, famous puzzle. Simpson's paradox is when a pattern that holds in every subgroup reverses once you pool the groups together. It looks like a contradiction. It is really just confounding wearing a dramatic costume.
Here are recovery counts from a classic, real-shaped comparison of a new treatment against the old one for kidney stones. Patients came in two severities, mild and severe, and — crucially — doctors gave the aggressive new treatment more often to the severe cases. Look within each severity and the new treatment wins both times. Mild stones: the new treatment cured 81 of 87 (93%), the old cured 234 of 270 (87%). Severe stones: the new treatment cured 192 of 263 (73%), the old cured 55 of 80 (69%). Yet pool everyone and it flips: across all patients the new treatment cured 273 of 350 (78%) while the old cured 289 of 350 (83%). The new treatment is better for mild stones, better for severe stones, and somehow worse overall.
Why the flip? Severity is a confounder: it drives the treatment a patient receives (severe → new treatment) AND it drives recovery (severe → lower recovery). Draw it — severity → treatment, severity → recovery, treatment → recovery — and it is the very confounder triangle from earlier. The new treatment was handed the hard cases. The pooled 78% versus 83% is the open backdoor path talking, not the treatment.
So which number should guide a doctor? The within-severity ones. They compare like with like; the pooled number compares a mostly-severe group against a mostly-mild group. The earlier adjustment formula says exactly this: weight each severity's result by how common it is overall. The pooled rate cheats by weighting each treatment by its OWN case mix — and the two mixes are wildly different, which is what lets the comparison reverse.
The pooled rate weights each severity by Pr(z | new) — this arm's own case mix — while the old-treatment arm uses a different mix Pr(z | old). Different weights on the same per-group numbers are what produce the reversal. The adjustment formula instead uses the shared population mix Pr(z) for both arms, giving the honest comparison.
import pandas as pd
# one row per patient: severity ('mild'/'severe'), treatment ('new'/'old'), cured (1/0)
# Crude pooled cure rate — the MISLEADING number
df.groupby('treatment')['cured'].mean()
# Adjusted view — cure rate within each severity stratum (the honest comparison)
df.groupby(['severity', 'treatment'])['cured'].mean()An interactive chart comparing new versus old treatment recovery rates, shown first split by mild and severe stones (new treatment higher in both) and then pooled across all patients (new treatment lower), illustrating the reversal.
Drawing your own causal diagram
You do not need fancy software to get the benefit of all this — a napkin will do. The discipline of drawing the diagram BEFORE you run the regression is what protects you. Here is a workflow you can use on your next real question.
- Name the two stars: write down your treatment X (the thing whose effect you want) and your outcome Y, and draw the arrow you are trying to measure, X → Y.
- Brainstorm the other variables that matter — anything that might cause X, cause Y, or be caused by them. Add a node for each, even ones you did not measure.
- Draw an arrow for every direct cause you believe in, always from cause to effect. Resist drawing an arrow when you really only mean “they are correlated” — only direct causation earns an arrow. Then check there are no loops.
- Classify each extra variable by its shape: a common cause of X and Y (confounder), a step on the X → Y path (mediator), or a common effect (collider).
- Apply the backdoor criterion: list every backdoor path and pick a set Z that blocks them all — controlling confounders, while deliberately NOT controlling mediators or colliders (or anything downstream of X).
- Mark the confounders you could NOT measure. Each one is an honest hole in your analysis — name it in your write-up rather than hiding it.
Keep the whole arc in view. A diagram does not make a causal claim true; it makes your assumptions visible, and then tells you what to control for given those assumptions. The hardest threats — a confounder you never measured, an arrow you drew wrong — live in the assumptions, not the arithmetic. When you cannot measure your way around them, you reach for the designs in the next guide: natural experiments that let the world do the randomizing for you. Draw the arrows first; trust the numbers second.