Visual grammar: interactions and DiD SWIGs

DAGMakie keeps a small, fixed visual vocabulary for effect modification / interaction figures and difference-in-differences (DiD) single-world intervention graphs (SWIGs). The grammar extends the publication defaults in dag_theme / default_style: white ground, no axes, in-node labels, steel-blue fills, goldenrod confounders, seagreen mediators / effect nodes, and gray hollow latents.

Terms

  • SWIG (single-world intervention graph; Richardson & Robins, 2013): under $do(A = a)$, the intervened node is drawn as a split — a random half $A$ that still receives incoming edges, and a fixed half $a$ from which outgoing edges leave. Counterfactual labels such as $Y(0)$ live on this graph; the factual DAG alone does not carry them.
  • IDAG (interaction DAG): companion to an ordinary outcome DAG in which the outcome node is replaced by an effect-measure node (e.g. $δ$). Use it when the question is how an effect varies with a modifier, not when identifying $E[Y \mid do(A)]$.
Display only

Modifier edges, effect-measure nodes, and SWIG fixed halves are pedagogical annotations. They do not change d-separation or identification. Use CausalInference.jl / CausalDynamics.jl for those queries.

House rule

  1. Identification / adjustment → ordinary outcome DAG (steel-blue / goldenrod).
  2. “Does $G$ change the effect of $A$?” → IDAG companion (green effect-measure node), or a dash-dot mod edge with an explicit caption.
  3. “Is DiD justified?” → time-expanded factual DAG plus a SWIG for the untreated world (parallel trends lives on $Y_t(0)$, not on the factual DAG alone).

Node conventions

RoleNodeTypeFill / strokeMarker
Observed / defaultObserved:steelblue, stroke 1circle
TreatmentTreatment:steelblue, stroke 2.5circle
OutcomeOutcome:steelblue, stroke 2.0 darkgraycircle
Confounder / contextConfounder:goldenrodcircle
MediatorMediator:seagreencircle
LatentLatentgray / hollow, stroke 2circle
Effect measure (IDAG)EffectMeasure:seagreenrect
SWIG fixed halfSwigFixedwhite, stroke 2, black labelrect

Treatment and outcome stay in the steel-blue family so default / minimal / bold / presentation themes keep working; roles are stroke and shape, not a new rainbow of fills.

Edge conventions

KindStyleNotes
Causal $→$solid blackusual GraphMakie arrows
Latent confounding $↔$dashed curveMixedGraph
Removed by $do(·)$dashed, lightintervention plots
Modifier annotationdash-dot, :darkgraymodifier_edge; caption required
using DAGMakie

e = modifier_edge(1, 3)
(e.type, e.style, e.color, e.label)
(Modifier, :dashdot, :darkgray, "mod")

Example 1 — Vaccine × nutrition

Nutrition $N$ confounds vaccination $V$ and outcome $Y$, and may also modify the vaccine effect on an additive scale. The left panel is the outcome DAG for identification; the right panel is an IDAG where $Y$ is replaced by an effect-measure node $δ$ (Nilsson et al. style).

using DAGMakie, CairoMakie

fig = with_theme(dag_theme()) do
    dagplot_vaccine_nutrition_interaction()
end
fig
Example block output

Constructors if you need the specs separately:

Caption pattern: Left: structural DAG for identifying $E[Y \mid do(V)]$ after adjusting for $N$. Right: IDAG for additive effect modification; the effect node is not an outcome random variable.

Example 2 — Canonical 2×2 DiD SWIG

Two groups $G$, two periods, treatment $A_1$ only for the treated group in period 1, with unit-level latent $U$. The left panel is the factual time-expanded DAG; the right panel is a SWIG under $do(A_1 = 0)$ (split node as above). Incoming edges stay on the random half $A_1$; outflows leave from the fixed half $a=0$, and the post-period outcome is labelled $Y_1(0)$.

using DAGMakie, CairoMakie

fig = with_theme(dag_theme()) do
    dagplot_did_swig()
end
fig
Example block output

Constructors:

Caption pattern: Left: two-period DAG with unit-level $U$. Right: SWIG for the untreated world; parallel trends is a statement about $Y_t(0)$, read on the SWIG.

Do not draw two-way fixed-effect dummies as causal nodes; show substantive latents (here $U$) instead.

Side-by-side companions

dagplot_side_by_side is the shared layout for outcome | IDAG and factual | SWIG pairs (same habit as dagplot_do_comparison).

using DAGMakie, CairoMakie

left = vaccine_nutrition_outcome_spec()
right = vaccine_nutrition_idag_spec()
fig = with_theme(dag_theme()) do
    dagplot_side_by_side(
        left, right;
        titles = ("Outcome DAG", "IDAG"),
        layout = vaccine_nutrition_layout(),
    )
end
fig
Example block output