The causal graph view

Declaring a treatment/outcome/confounder structure and reading its true, closed-form ATE.

The engine already resolves a dependency graph and already lets any column be a function of other columns plus a random term, which is a structural causal model in everything but name. The Causal view is the interface for that — declaring a role wires the edges for you, and the panel shows the true, closed-form treatment effect a real estimator would have to recover from noisy data.

Declaring a model#

Open a table, switch to the Causal view, and click Declare a causal model. Two roles are required — Treatment and Outcome — plus a Direct effect, the coefficient of the edge between them. Everything else (confounders, mediators, colliders, instruments) is optional, and each one is a real column from this table, not a new synthetic node:

Role What declaring it wires Needs its own variance?
Confounder An edge into treatment and into outcome Yes
Mediator An edge from treatment and into outcome No
Collider An edge from treatment and from outcome No
Instrument An edge into treatment only Yes

You never draw an arrow yourself — declaring a confounder is what creates both of its edges, into treatment and into outcome, at the strengths you set.

Why a column needs a stated variance#

Treatment, confounders, and instruments each need a real, known variance for the ATE arithmetic to be exact rather than simulated. Most numeric types state one — but a truncated distribution's variance is not its family's own stated one (a normal cut at two standard deviations has less spread than the untruncated family), so a treatment or confounder column using a truncated distribution is refused with a real, specific validation error, not a silent approximation. numeric.currency and numeric.percentage's own default examples are untruncated and work directly; numeric.decimal's default example is truncated and will not qualify for these two roles until its truncation is removed.

Reading the panel#

Once a model validates, the graph and the numbers both come from the engine's own causal.truth() — nothing here is recomputed in the UI:

A declared causal model: column4 confounds column2 (treatment) and column3 (outcome), with a 0.4 direct effect

  • True average treatment effect — the real coefficient you declared (Direct effect, 0.4000 above), not an estimate.
  • Naive regression estimate — what a plain regression of outcome on treatment would report, confounding included (0.4247 above).
  • Confounding — the gap between them (0.0247 above): exactly what the confounder's own 0.5/0.3 coefficients and its variance predict, in closed form.

This is the number a downstream user's own naive analysis would get wrong, printed next to the number it should have gotten — the whole point of declaring the structure instead of leaving it implicit in a distribution nobody wrote down.