API Reference

Layout utilities

DAGMakie.time_indexed_layoutFunction
time_indexed_layout(n_variables, n_times; dx=2.0, dy=1.5, origin=(0.0, 0.0))

Grid layout for time-unrolled causal graphs.

Node order must be outer loop over occasions t = 1:n_times and inner loop over variables v = 1:n_variables (the indexing used by CausalDynamics unroll_temporal_dag). Column t is at x = origin[1] + (t - 1) * dx; variable row v is at y = origin[2] - (v - 1) * dy.

Returns

  • Vector{Point2f} of length n_variables * n_times
source
DAGMakie.digraph_skeletonFunction
digraph_skeleton(g::AbstractGraph) -> SimpleGraph

Undirected skeleton of a directed graph: keep an undirected edge {i,j} whenever g has i → j, j → i, or both.

Useful for CPDAG-style PC output that encodes an undirected edge as a pair of opposing directed edges. Prefer this over plotting both arrows. See dagplot_skeleton for a ready-made figure with undirected stroke styling.

source
DAGMakie.graph_from_structural_matrixFunction
graph_from_structural_matrix(B; atol=0.0)

Build a SimpleDiGraph from a structural parameter matrix B (linear SEM / linear SCM weights), including self-loops for non-zero diagonal entries.

Convention matches structural_edge_labels: B[i, j] is the weight of node j in the assignment for node i, so a non-zero entry yields the directed edge j → i. In particular, B[i, i] ≠ 0 yields a self-loop at i, which GraphMakie draws as a self-pointing arc.

Entries with absolute value at most atol are treated as absent.

Arguments

  • B: Square structural weight matrix
  • atol: Absolute tolerance for treating entries as zero

Returns

  • SimpleDiGraph with one edge per non-zero structural weight

Example

B = [0.0 0.0 0.0; 0.8 0.0 0.0; 0.5 1.2 3.0]
g = graph_from_structural_matrix(B)  # edges 1→2, 1→3, 2→3, and self-loop 3→3
source
DAGMakie.ensure_structural_self_loops!Function
ensure_structural_self_loops!(g, B; atol=0.0)

Add missing self-loops i → i whenever |B[i, i]| > atol. Mutates g and returns it. Off-diagonal structure is left unchanged.

Used by structural_edge_labels when ensure_self_loops=true (the default) so a non-zero diagonal is enough to draw self-pointing arcs.

source
DAGMakie.structural_edge_labelsFunction
structural_edge_labels(g, B; latex=true, digits=2, ensure_self_loops=true)

Build GraphMakie elabels for g from a structural parameter matrix B (linear SEM / linear SCM weights).

Convention: B[i, j] is the structural weight of node j in the assignment for node i, i.e. the parameter on the directed edge j → i (including self-loops when i == j). Labels are returned in Graphs.edges(g) order (the order GraphMakie expects for elabels).

When ensure_self_loops=true (default), non-zero diagonal entries that are missing from g are added in place via ensure_structural_self_loops! before labels are built. Off-diagonal non-zeros without a matching edge still warn (and are omitted from labels). You can also build the full edge set with graph_from_structural_matrix.

This is deliberately not named “effects”: in the Pearl ladder an effect is usually an interventional or counterfactual estimand, whereas these labels are structural parameters (or other short mechanism annotations) on the graph.

When latex=true (default), each entry is a Makie LaTeXString so parameters render as maths on the edge. Set latex=false for plain String labels.

Examples

g, labels = confounding_graph(["Z", "X", "Y"])
B = [0 0 0; 0.8 0 0; 0.5 1.2 3.0]  # diagonal 3.0 → self-loop on Y
fig, ax, p = dagplot(g;
    nlabels = labels,
    elabels = structural_edge_labels(g, B),  # adds Y → Y automatically
    elabels_fontsize = 14,
    elabels_distance = 12,
    elabels_rotation = 0,
)
source
structural_edge_labels(g, labels; latex=false)

Pass edge annotations already ordered as Graphs.edges(g). With latex=true, wrap plain strings (or numbers) as LaTeXString via Makie.latexstring so short TeX such as "\\beta_{ZX}" or a fragment of a structural assignment renders on the edge.

source

Main Plotting Functions

DAGMakie.dagplotFunction
dagplot(g; kwargs...)

Create a clean DAG visualisation with publication-ready defaults.

This is a convenience wrapper that creates a new figure and axis, then delegates to dagplot!. All keyword arguments are passed through.

Arguments

  • g::AbstractGraph: A graph from Graphs.jl to plot

Keyword Arguments

  • figure_size::Tuple{Int, Int} = (600, 400): Figure dimensions
  • See dagplot! for all other keyword arguments

Returns

  • Tuple (fig, ax, p) where:
    • fig: The Figure object
    • ax: The Axis object
    • p: The GraphPlot object (for accessing node positions, etc.)

Examples

using Graphs, DAGMakie, CairoMakie

# Simple chain graph
g = SimpleDiGraph(3)
add_edge!(g, 1, 2)
add_edge!(g, 2, 3)

fig, ax, p = dagplot(g, nlabels=["X", "Y", "Z"])
save("dag.png", fig)

# Access node positions
positions = p[:node_pos][]
source
dagplot(spec::DAGSpec; kwargs...)

Plot a DAG from a DAGSpec specification.

Arguments

  • spec::DAGSpec: A DAG specification with graph, nodes, and edges

Keyword Arguments

  • Same as dagplot(g; kwargs...)

Returns

  • Tuple (fig, ax, p)
source
dagplot(mg::MixedGraph; kwargs...)

Create a DAG visualisation with both directed and bidirected edges.

Bidirected edges (↔) are rendered as curved arcs with double arrowheads, representing unmeasured common causes (latent confounders).

Additional Keyword Arguments

  • bidirected_color = :gray: Colour for bidirected edges
  • bidirected_width = 1.0: Line width for bidirected edges
  • bidirected_style = :dash: Line style for bidirected edges
  • bidirected_curvature = 0.3: Curvature of bidirected arcs
  • bidirected_arrow_size = 8: Size of arrowheads on bidirected edges

Examples

using DAGMakie, CairoMakie

# Confounded treatment-outcome
mg = mixed_graph(2, [(1, 2)], [(1, 2)])  # X → Y with X ↔ Y
fig, ax, p = dagplot(mg, nlabels=["X", "Y"])

# Instrumental variable with confounding
mg = mixed_graph(3, [(1, 2), (2, 3)], [(2, 3)])  # Z → X → Y, X ↔ Y
fig, ax, p = dagplot(mg, nlabels=["Z", "X", "Y"])
source
DAGMakie.dagplot!Function
dagplot!(ax, g; kwargs...)

Plot a DAG into an existing axis with clean, publication-ready styling.

Automatically hides axis decorations, applies DataAspect, and sets appropriate limits to prevent clipping of nodes and labels.

Arguments

  • ax: A Makie Axis object
  • g::AbstractGraph: A graph from Graphs.jl to plot

Layout Keyword Arguments

  • layout = Spring(): Layout algorithm from NetworkLayout.jl
  • padding::Float64 = 0.1: Padding around graph as fraction of range

Node Keyword Arguments

  • node_size = 12: Node marker size in pixels
  • node_color = DEFAULT_NODE_COLOR: Node fill colour (single value or vector; steel-blue by default)
  • node_strokewidth = 1.0: Node outline width
  • node_strokecolor = :black: Node outline colour
  • node_marker = :circle: Node marker shape

Edge Keyword Arguments

  • edge_color = :black: Edge colour
  • edge_width = 1.0: Edge line width
  • arrow_size = 10: Arrowhead size
  • arrow_shift = :end: Arrow position (:end or Float64 0-1)
  • elabels = nothing: Edge labels in Graphs.edges(g) order. Accepts plain Strings or Makie LaTeXStrings (e.g. from structural_edge_labels with latex=true) for structural parameters or short mechanism TeX on edges
  • elabels_fontsize, elabels_distance, elabels_rotation, elabels_side, … : forwarded to GraphMakie (use elabels_rotation = 0 to keep maths upright)
  • selfedge_size, selfedge_direction, selfedge_width: GraphMakie self-loop geometry. When the graph has self-loops and selfedge_size is omitted, DAGMakie defaults to DEFAULT_SELFEDGE_SIZE (compact beside the node)

Label Keyword Arguments

  • nlabels = nothing: Node labels (vector of strings / LaTeXStrings, or nothing)
  • nlabels_align = (:center, :center): Makie text-box anchor (or vector of anchors). Named sides are edges of the label, not “label goes this side of the node”: (:left, :center) left-anchors the text so it sits to the right of the node. See the Label Alignment guide.
  • auto_align_labels = false: When true, place labels outside nodes in the largest angular gap (sets a positive distance and dark label colour unless you override them)
  • nlabels_distance = 0: Label distance from node in pixels along nlabels_align (0 centres labels in nodes; with (:center, :center) the offset is zero regardless of distance—use a non-centred align or auto_align_labels=true for outside labels)
  • nlabels_fontsize = 16: Label font size
  • nlabels_color = :white: Label colour (white on dark node fills)

Smart / dagitty colouring

  • smart = false: Set true / :ancestors for dagitty-style ancestor colours, or :adjustment to also emphasise a backdoor adjustment set (needs CausalInference or adjustment=)
  • treatment, outcome: Exposure and outcome node indices (required when smart is on)
  • adjustment: Optional Set{Int} for smart=:adjustment

Additional Arguments

  • Additional keyword arguments are passed to GraphMakie.graphplot!

Returns

  • The GraphPlot object

Examples

using Graphs, DAGMakie, CairoMakie

g = SimpleDiGraph(3)
add_edge!(g, 1, 2)
add_edge!(g, 1, 3)
add_edge!(g, 2, 3)

# Simple usage
fig = Figure()
ax = Axis(fig[1, 1])
dagplot!(ax, g, nlabels=["Z", "X", "Y"])

# With node colours indicating roles
dagplot!(ax, g, 
    nlabels=["Confounder", "Treatment", "Outcome"],
    node_color=[NODE_COLOR_CONFOUNDER, DEFAULT_NODE_COLOR, DEFAULT_NODE_COLOR]
)

# Multiple DAGs in one figure
fig = Figure(size=(1200, 400))
ax1, ax2, ax3 = Axis(fig[1, 1]), Axis(fig[1, 2]), Axis(fig[1, 3])
dagplot!(ax1, g1, nlabels=["A", "B", "C"])
dagplot!(ax2, g2, nlabels=["X", "Y", "Z"])
dagplot!(ax3, g3, nlabels=["P", "Q", "R"])
source
dagplot!(ax, spec::DAGSpec; kwargs...)

Plot a DAG from a DAGSpec specification into an existing axis.

source
dagplot!(ax, mg::MixedGraph; kwargs...)

Plot a MixedGraph (with bidirected edges) into an existing axis.

source

Convenience Pattern Functions

DAGMakie.dagplot_chainFunction
dagplot_chain(labels; kwargs...)

Plot a simple chain DAG: X₁ → X₂ → ... → Xₙ

Arguments

  • labels::Vector{String}: Labels for each node in the chain
  • kwargs...: Additional arguments passed to dagplot

Returns

  • Tuple (fig, ax, p)
source
DAGMakie.dagplot_forkFunction
dagplot_fork(labels; kwargs...)

Plot a fork DAG: X₁ ← X₂ → X₃

Arguments

  • labels::Vector{String}: Labels [left, fork, right]
  • kwargs...: Additional arguments passed to dagplot
source
DAGMakie.dagplot_colliderFunction
dagplot_collider(labels; kwargs...)

Plot a collider DAG: X₁ → X₂ ← X₃

Arguments

  • labels::Vector{String}: Labels [left, collider, right]
  • kwargs...: Additional arguments passed to dagplot
source
DAGMakie.dagplot_confoundingFunction
dagplot_confounding(labels; kwargs...)

Plot a confounding DAG: Z → X → Y, Z → Y.

Uses a triangle layout by default (confounder on top, treatment and outcome below). Layered left-to-right layout places one node per layer and draws the backdoor edge through the treatment node; pass layout to override.

Arguments

  • labels::Vector{String}: Labels [confounder, treatment, outcome]
  • kwargs...: Additional arguments passed to dagplot
source
DAGMakie.dagplot_mediationFunction
dagplot_mediation(labels; kwargs...)

Plot a mediation DAG: X → M → Y, X → Y.

Uses a triangle layout by default (mediator on top, treatment and outcome below). Layered left-to-right layout places one node per layer and draws the direct effect through the mediator; pass layout to override.

Arguments

  • labels::Vector{String}: Labels [treatment, mediator, outcome]
  • kwargs...: Additional arguments passed to dagplot
source
DAGMakie.dagplot_confoundedFunction
dagplot_confounded(labels; kwargs...)

Plot a simple confounded graph: X → Y with X ↔ Y.

Uses a horizontal layout by default so the bidirected arc is visible above the directed edge; pass layout to override.

Arguments

  • labels::Vector{String}: Labels [treatment, outcome]
source
DAGMakie.dagplot_frontdoorFunction
dagplot_frontdoor(labels; kwargs...)

Plot a frontdoor criterion graph: X → M → Y with X ↔ Y.

Uses a triangle layout by default (mediator on top). Layered layout collinearises the three nodes and obscures the bidirected confounding arc; pass layout to override.

Arguments

  • labels::Vector{String}: Labels [treatment, mediator, outcome]
source
DAGMakie.dagplot_iv_confoundedFunction
dagplot_iv_confounded(labels; kwargs...)

Plot an IV graph with confounding: Z → X → Y, X ↔ Y.

Uses a triangle layout by default (instrument on top). Layered layout collinearises the chain and hides the bidirected confounding arc; pass layout to override.

Arguments

  • labels::Vector{String}: Labels [instrument, treatment, outcome]
source
DAGMakie.dagplot_m_biasFunction
dagplot_m_bias(labels; kwargs...)

Plot the classic five-node M-bias DAG with explicit latents:

U₁ → X, U₁ → M, U₂ → M, U₂ → Y

Default layout forms the letter M (latents on top, $X$$M$$Y$ on the base). Pass layout to override.

Arguments

  • labels::Vector{<:AbstractString}: Labels $[U₁, U₂, X, M, Y]$
source

Types

Node Types

DAGMakie.NodeTypeType
NodeType

Enumeration of node types in causal diagrams.

Values

  • Observed: Measured variable (filled circle)
  • Latent: Unmeasured variable (hollow circle or dashed outline)
  • Treatment: Treatment/exposure variable (thick stroke)
  • Outcome: Outcome variable (emphasised stroke)
  • Instrument: Instrumental variable
  • Confounder: Confounding variable
  • Mediator: Mediating variable
  • Collider: Collider variable
  • EffectMeasure: Causal-effect node on an IDAG (not a factual outcome)
  • SwigFixed: Fixed half of a SWIG split node (intervened value)
source
DAGMakie.NodeSpecType
NodeSpec

Specification for a single node in a DAG.

Fields

  • label::String: Display label for the node
  • type::NodeType: Type of node (determines styling)
  • color: Override colour (nothing = use type default)
  • size::Union{Real, Tuple, Nothing}: Override size (nothing = use default; a (width, height) tuple is passed through to Makie for non-square markers)
source

Edge Types

DAGMakie.EdgeTypeType
EdgeType

Enumeration of edge types in causal diagrams.

Values

  • Directed: Standard directed edge (→)
  • Bidirected: Bidirected edge for unmeasured confounding (↔)
  • Undirected: Undirected edge (—)
  • Modifier: Pedagogical annotation that one variable modifies an effect (dash-dot; not an input to d-separation)
source
DAGMakie.EdgeSpecType
EdgeSpec

Specification for a single edge in a DAG.

Fields

  • src::Int: Source node index
  • dst::Int: Destination node index
  • type::EdgeType: Type of edge
  • color: Override colour
  • width::Union{Real, Nothing}: Override line width
  • style::Union{Symbol, Nothing}: Line style (:solid, :dash, :dot, etc.)
  • label::Union{String, Nothing}: Optional edge label
source

DAG Specification

DAGMakie.DAGSpecType
DAGSpec

Complete specification for a causal DAG visualisation.

Fields

  • graph::AbstractGraph: The underlying graph structure
  • nodes::Vector{NodeSpec}: Node specifications (one per vertex)
  • edges::Vector{EdgeSpec}: Edge specifications (overrides for specific edges)
  • title::Union{String, Nothing}: Optional title for the DAG
source

Mixed Graphs

DAGMakie.MixedGraphType
MixedGraph

A graph that supports both directed edges (→) and bidirected edges (↔).

Bidirected edges represent unmeasured common causes (latent confounders) in causal diagrams. Internally, bidirected edges are stored separately from the directed graph structure.

Fields

  • directed::SimpleDiGraph: The directed edges
  • bidirected::Set{Tuple{Int, Int}}: Set of bidirected edge pairs (unordered)

Examples

# Create a mixed graph with confounding
mg = MixedGraph(3)
add_directed_edge!(mg, 1, 3)  # X → Y
add_directed_edge!(mg, 2, 3)  # Z → Y
add_bidirected_edge!(mg, 1, 2)  # X ↔ Z (unmeasured confounder)

# Or from existing directed graph
g = SimpleDiGraph(3)
add_edge!(g, 1, 3)
add_edge!(g, 2, 3)
mg = MixedGraph(g, [(1, 2)])  # Add bidirected edge
source

Paths

DAGMakie.CausalPathType
CausalPath

A path through a DAG, tracking nodes and edge directions (forward or backward along graph edges).

Fields

  • nodes::Vector{Int}: Sequence of nodes
  • directions::Vector{Symbol}: Direction of each edge (:forward or :backward)
source
DAGMakie.PathSegmentType
PathSegment

A segment of a path, tracking the direction of traversal.

Fields

  • node::Int: The node in this segment
  • direction::Symbol: :forward (→), :backward (←), or :start
source
DAGMakie.path_edgesFunction
path_edges(path::CausalPath)

Return the edges in a path as (src, dst) tuples in graph orientation.

source

Interventions

DAGMakie.InterventionType
Intervention

Specification for an intervention (do(·)) on one or more variables.

Fields

  • nodes::Vector{Int}: Nodes being intervened on
  • values::Vector{String}: Display values for the interventions (e.g., "x=1")
  • label::String: Overall intervention label (e.g., "do(X=1)")
source
DAGMakie.CausalQueryType
CausalQuery

A query about a causal effect.

Fields

  • treatment::Int: Treatment variable
  • outcome::Int: Outcome variable
  • intervention::Union{Intervention, Nothing}: Intervention (if specified)
  • conditioning::Set{Int}: Variables to condition on
source

Graph Construction

DAGMakie.chain_graphFunction
chain_graph(labels::Vector{String})

Create a simple chain DAG: X₁ → X₂ → ... → Xₙ

Arguments

  • labels: Vector of node labels

Returns

  • Tuple (g, labels) where g is the graph
source
DAGMakie.fork_graphFunction
fork_graph(labels::Vector{String})

Create a fork DAG: X₁ ← X₂ → X₃ (with X₂ as the fork point)

Assumes 3 nodes: labels[1] ← labels[2] → labels[3]

Arguments

  • labels: Vector of 3 node labels

Returns

  • Tuple (g, labels) where g is the graph
source
DAGMakie.collider_graphFunction
collider_graph(labels::Vector{String})

Create a collider DAG: X₁ → X₂ ← X₃ (with X₂ as the collider)

Assumes 3 nodes: labels[1] → labels[2] ← labels[3]

Arguments

  • labels: Vector of 3 node labels

Returns

  • Tuple (g, labels) where g is the graph
source
DAGMakie.confounding_graphFunction
confounding_graph(labels::Vector{String})

Create a classic confounding DAG: Z → X → Y, Z → Y

Assumes 3 nodes: Z (confounder), X (treatment), Y (outcome)

Arguments

  • labels: Vector of 3 node labels [Z, X, Y]

Returns

  • Tuple (g, labels) where g is the graph
source
DAGMakie.mediation_graphFunction
mediation_graph(labels::Vector{String})

Create a mediation DAG: X → M → Y, X → Y

Assumes 3 nodes: X (treatment), M (mediator), Y (outcome)

Arguments

  • labels: Vector of 3 node labels [X, M, Y]

Returns

  • Tuple (g, labels) where g is the graph
source
DAGMakie.instrumental_graphFunction
instrumental_graph(labels::Vector{String})

Create an instrumental variable DAG: Z → X → Y, U → X, U → Y

Assumes 4 nodes: Z (instrument), X (treatment), Y (outcome), U (unobserved confounder)

Arguments

  • labels: Vector of 4 node labels [Z, X, Y, U]

Returns

  • Tuple (g, labels) where g is the graph
source
DAGMakie.mixed_graphFunction
mixed_graph(n::Int, directed_edges, bidirected_edges)

Create a MixedGraph with specified directed and bidirected edges.

Arguments

  • n::Int: Number of vertices
  • directed_edges: Iterable of (src, dst) pairs for directed edges
  • bidirected_edges: Iterable of (i, j) pairs for bidirected edges

Examples

# Confounded treatment-outcome graph
mg = mixed_graph(2, [(1, 2)], [(1, 2)])  # X → Y with X ↔ Y

# Instrumental variable setup
mg = mixed_graph(4,
    [(1, 2), (2, 3)],  # Z → X → Y
    [(2, 3)]           # X ↔ Y (unmeasured confounding)
)
source
DAGMakie.confounded_graphFunction
confounded_graph(labels::Vector{String})

Create a simple confounded graph: X → Y with X ↔ Y (unmeasured confounder).

Arguments

  • labels: Vector of 2 labels [treatment, outcome]

Returns

  • Tuple (mg, labels) where mg is a MixedGraph
source
DAGMakie.frontdoor_graphFunction
frontdoor_graph(labels::Vector{String})

Create a frontdoor criterion graph: X → M → Y with X ↔ Y.

Arguments

  • labels: Vector of 3 labels [treatment, mediator, outcome]

Returns

  • Tuple (mg, labels) where mg is a MixedGraph
source
DAGMakie.iv_confounded_graphFunction
iv_confounded_graph(labels::Vector{String})

Create an instrumental variable graph with confounding: Z → X → Y, X ↔ Y.

Arguments

  • labels: Vector of 3 labels [instrument, treatment, outcome]

Returns

  • Tuple (mg, labels) where mg is a MixedGraph
source
DAGMakie.m_bias_graphFunction
m_bias_graph(labels)

Create the classic five-node M-bias DAG with explicit latents:

U₁ → X, U₁ → M, U₂ → M, U₂ → Y

No directed edge $X → Y$ (pure M-bias). Conditioning on the collider $M$ opens the non-causal path $X ← U₁ → M ← U₂ → Y$.

Arguments

  • labels: Vector of 5 labels $[U₁, U₂, X, M, Y]$ (defaults to those symbols)

Returns

  • Tuple (mg, labels) where mg is a MixedGraph with no bidirected edges (latents are drawn explicitly rather than as $X ↔ M ↔ Y$)
source
DAGMakie.m_bias_specFunction
m_bias_spec(labels)

DAGSpec for the five-node M-bias DAG with latent / collider styling.

Same edges as m_bias_graph: $U₁ → X$, $U₁ → M$, $U₂ → M$, $U₂ → Y$.

source

Highlighting

DAGMakie.HighlightSpecType
HighlightSpec

Specification for highlighting elements in a DAG plot.

Fields

  • nodes::Vector{Int}: Nodes to highlight
  • node_colors::Vector: Colors for highlighted nodes
  • edges::Vector{Tuple{Int,Int}}: Edges to highlight (as src, dst pairs)
  • edge_colors::Vector: Colors for highlighted edges
  • labels::Vector{String}: Optional labels for highlighted nodes
source
DAGMakie.highlight_from_pathsFunction
highlight_from_paths(paths::Vector{CausalPath}; colors=nothing)

Create a HighlightSpec from multiple paths with different colors.

source
DAGMakie.highlight_adjustment_setFunction
highlight_adjustment_set(g::AbstractGraph, treatment::Int, outcome::Int, 
                         adjustment::Set{Int}; treatment_color=:green, 
                         outcome_color=:blue, adjustment_color=:orange)

Create a HighlightSpec for visualising an adjustment set.

source
DAGMakie.highlight_backdoor_pathsFunction
highlight_backdoor_paths(paths::Vector{CausalPath};
                         blocked_color=:gray, open_color=:red,
                         blocked::AbstractVector{Bool}=Bool[])

Create a HighlightSpec from precomputed backdoor paths.

Pass blocked of the same length as paths to colour blocked vs open paths. If blocked is empty, all paths use open_color.

Path finding belongs in CausalInference.jl / CausalDynamics.jl — not DAGMakie.

source
DAGMakie.dagplot_highlightedFunction
dagplot_highlighted(g::AbstractGraph, highlight::HighlightSpec; kwargs...)

Create a DAG plot with highlighted elements.

Arguments

  • g: The graph
  • highlight: HighlightSpec defining what to highlight
  • kwargs...: Additional arguments passed to dagplot

Examples

g = confounding_graph(["Z", "X", "Y"])[1]
path = CausalPath([1, 2, 3]; directions = [:forward, :forward])
highlight = highlight_from_path(path, color=:red)
fig, ax, p = dagplot_highlighted(g, highlight, nlabels=["Z", "X", "Y"])
source
DAGMakie.dagplot_backdoorFunction
dagplot_backdoor(g, treatment, outcome; adjustment=Set{Int}(), paths=CausalPath[], kwargs...)

Plot a DAG highlighting treatment, outcome, an adjustment set, and optional precomputed backdoor paths.

For automatic adjustment / d-separation from CausalInference.jl, load that package so DAGMakieCausalInferenceExt activates, or pass adjustment / paths explicitly from CausalDynamics.jl.

source
DAGMakie.dagplot_dsepFunction
dagplot_dsep(g, x, y, z; separated, kwargs...)

Plot a DAG with X, Y, and conditioning set Z highlighted.

Pass separated::Bool for the axis title (from CausalInference.jl dsep or CausalDynamics.jl d_separated). With using CausalInference, separated defaults via the package extension when omitted.

source
DAGMakie.dagplot_adjustmentFunction
dagplot_adjustment(g, treatment, outcome; adjustment=nothing, kwargs...)

Plot a DAG with an adjustment set. Pass adjustment::Set{Int} explicitly, or using CausalInference so the extension can compute a minimal backdoor set.

source

Interventions

DAGMakie.do_surgeryFunction
do_surgery(g::AbstractGraph, intervention_nodes::Vector{Int})

Perform display-only graph surgery for do(·): remove all incoming edges to intervention nodes. Use this to draw mutilated DAGs in Makie; it is not an identification API.

For CausalDynamics identification and CDM interventions, use DoIntervention / apply_intervention (and related helpers) in CausalDynamics.jl.

Returns a graph where intervened nodes no longer have parents (for plotting).

Arguments

  • g: Original DAG
  • intervention_nodes: Nodes being intervened on (set to fixed values)

Returns

  • New SimpleDiGraph with incoming edges to intervention nodes removed

Examples

# Confounding: Z → X → Y, Z → Y
g = SimpleDiGraph(3)
add_edge!(g, 1, 2)  # Z → X
add_edge!(g, 2, 3)  # X → Y
add_edge!(g, 1, 3)  # Z → Y

# do(X) - intervene on X
g_do = do_surgery(g, [2])
# Result: X → Y, Z → Y (Z → X edge removed)
source
do_surgery(g::AbstractGraph, intervention_node::Int)

Single-node intervention convenience method.

source
DAGMakie.do_surgery!Function
do_surgery!(g::SimpleDiGraph, intervention_nodes::Vector{Int})

In-place display-only graph surgery — modifies the original graph. Same boundary as do_surgery: for plotting mutilated DAGs, not CausalDynamics ID.

source
DAGMakie.dagplot_interventionFunction
dagplot_intervention(g::AbstractGraph, intervention::Intervention;
                     show_original::Bool=true, kwargs...)

Plot a DAG with intervention applied, showing removed edges.

Arguments

  • g: Original DAG
  • intervention: Intervention specification
  • show_original: If true, show original edges as dashed
  • kwargs...: Additional arguments for dagplot

Examples

g, labels = confounding_graph(["Z", "X", "Y"])
int = Intervention(2)  # do(X)
fig, ax, p = dagplot_intervention(g, int, nlabels=labels)
source
DAGMakie.dagplot_doFunction
dagplot_do(g::AbstractGraph, intervention_node::Int; nlabels=nothing, kwargs...)

Convenience function for single-node intervention visualisation.

source
DAGMakie.dagplot_comparisonFunction
dagplot_comparison(g::AbstractGraph, intervention::Intervention;
                   nlabels=nothing, kwargs...)

Create a side-by-side comparison of original and post-intervention DAGs.

Returns

  • Figure with two panels: original (left) and post-intervention (right)
source
DAGMakie.dagplot_do_comparisonFunction
dagplot_do_comparison(g::AbstractGraph, intervention_node::Int;
                      nlabels=nothing, kwargs...)

Convenience function for side-by-side comparison with single intervention.

source
DAGMakie.query_to_stringFunction
query_to_string(query::CausalQuery, nlabels::Vector{String})

Convert a causal query to readable notation for plot titles.

source

Visual grammar (interactions / DiD)

DAGMakie.modifier_edgeFunction
modifier_edge(src, dst; kwargs...)

Create a pedagogical modifier EdgeSpec (dash-dot, dark gray).

Modifier edges annotate that one variable modifies an effect; they are not inputs to d-separation. Prefer an IDAG (vaccine_nutrition_idag_spec) when the claim is about effect variation.

source
DAGMakie.dagplot_side_by_sideFunction
dagplot_side_by_side(left, right; titles, figure_size, kwargs...)

Plot two DAGSpec (or graph) panels with shared keyword overrides.

Used for outcome-DAG | IDAG and factual-DAG | SWIG companion figures.

source
DAGMakie.vaccine_nutrition_idag_specFunction
vaccine_nutrition_idag_spec(; labels)

IDAG companion: replace $Y$ with an additive effect-measure node $δ$.

Arrows into $δ$ show which variables drive effect size (Nilsson et al. style).

source
DAGMakie.did_2x2_factual_specFunction
did_2x2_factual_spec(; labels)

Time-expanded factual DAG for a two-group, two-period DiD design.

Nodes (default order): $G$, $U$, $Y₀$, $Y₁$, $A₁$.

source
DAGMakie.did_2x2_swig_specFunction
did_2x2_swig_spec(; labels)

SWIG under $do(A₁ = 0)$ for the untreated world used by parallel trends.

Nodes (default order): $G$, $U$, $Y₀$, $Y₁(0)$, $A₁$, $a=0$. Incoming edges stay on the random half $A₁$; outflows leave from the fixed half $a=0$.

source

Smart / dagitty colouring

DAGMakie.SmartNodeRoleType
SmartNodeRole

Structural role of a node relative to a chosen exposure and outcome (dagitty- style ancestor highlighting).

source
DAGMakie.smart_style_for_graphFunction
smart_style_for_graph(g, treatment, outcome; mode=:ancestors, adjustment=nothing)

Return a named tuple of per-node styling for dagitty-like smart colouring.

Modes

  • :ancestors — exposure / outcome / ancestor-of-X / ancestor-of-Y / both / gray
  • :adjustment — same, plus thicker stroke on a backdoor adjustment set (requires CausalInference, or pass adjustment::Set{Int})
source
DAGMakie.ancestor_setsFunction
ancestor_sets(g, treatment, outcome) -> (anc_treatment, anc_outcome)

Return ancestor sets of treatment and outcome. Uses CausalInference.jl when the extension is loaded; otherwise Graphs reverse-BFS.

source
DAGMakie.ancestors_via_graphsFunction
ancestors_via_graphs(g, seeds) -> Set{Int}

Ancestors of seeds including the seeds themselves, via reverse BFS on inneighbors (Graphs only; no CausalInference).

source

Label Alignment

DAGMakie.compute_auto_label_alignsFunction
compute_auto_label_aligns(g::AbstractGraph, node_positions::AbstractVector)

Compute optimal label alignment for each node to avoid edge overlaps.

For each node, finds the largest angular gap between incident edges and places the label in the middle of that gap. This ensures labels do not overlap with edges, improving readability of causal diagrams.

Arguments

  • g::AbstractGraph: A graph from Graphs.jl (directed or undirected)
  • node_positions::AbstractVector: Vector of node positions (Point2f or similar)

Returns

  • Vector{Tuple{Symbol, Symbol}}: Alignment tuples (:horizontal, :vertical) for each node, suitable for use with nlabels_align in GraphMakie.

Algorithm

  1. For each node, collect angles of all incident edges (both incoming and outgoing)
  2. Normalise angles to [0, 2π] and sort
  3. Find the largest angular gap between adjacent edges (including wrap-around)
  4. Place label in the middle of the largest gap
  5. Map the gap midpoint angle to one of 8 alignment directions

Edge Cases

  • Isolated nodes (no incident edges): Returns (:right, :bottom) as default
  • Single edge: Places label opposite the edge direction
  • All edges in one direction: Places label in the opposite hemisphere

Examples

using Graphs, DAGMakie

g = SimpleDiGraph(3)
add_edge!(g, 1, 2)
add_edge!(g, 2, 3)

# Compute positions (e.g., from a layout algorithm)
positions = [Point2f(0, 0), Point2f(1, 0), Point2f(2, 0)]

# Get optimal alignments
aligns = compute_auto_label_aligns(g, positions)
# aligns[1] might be (:center, :top) since edge goes right
# aligns[2] might be (:center, :top) since edges go left and right
# aligns[3] might be (:center, :top) since edge goes left

Notes

  • Works with any layout algorithm (uses computed node positions)
  • Handles both directed and undirected graphs
  • Time complexity: O(V × E) where V is vertices and E is edges
  • Intended for outside-node labels: pair with a positive nlabels_distance (see resolve_auto_align_label_settings). With distance 0 the non-centred alignments only shift text inside the marker and look broken.
source
DAGMakie.align_to_directionFunction
align_to_direction(align::Tuple{Symbol, Symbol})

Convert a Makie text-box align to the offset direction GraphMakie uses with nlabels_distance.

Makie align names the edge of the label that sits on the anchor. Combined with a positive distance, that places the label on the opposite side of the node (e.g. (:left, :center) → offset east → label to the right of the node).

Arguments

  • align: Tuple of (:horizontal, :vertical) Makie text-align symbols

Returns

  • Direction from node toward the label (for use as distance .* dir)

Examples

align_to_direction((:left, :center))   # (1, 0)  — label ends up east of node
align_to_direction((:right, :center))  # (-1, 0) — label ends up west of node
align_to_direction((:center, :bottom)) # (0, 1)  — label ends up north of node
source
DAGMakie.resolve_auto_align_label_settingsFunction
resolve_auto_align_label_settings(g, positions; align, distance, color,
    distance_explicit, color_explicit)

Resolve label align / distance / colour when auto_align_labels=true.

Auto-align places labels in the largest angular gap outside the node. If the caller left the in-node defaults (distance == 0, white text), switch to AUTO_ALIGN_LABEL_DISTANCE and AUTO_ALIGN_LABEL_COLOR. Explicit nlabels_distance / nlabels_color from the caller are preserved.

source

Layout Utilities

DAGMakie.estimate_label_extentFunction
estimate_label_extent(label, align, fontsize, distance)

Estimate the extent of a label in pixel coordinates.

Returns a named tuple (dx_min, dx_max, dy_min, dy_max) representing the approximate bounding box offset from the node position.

Arguments

  • label::AbstractString: The label text
  • align::Tuple{Symbol, Symbol}: Alignment tuple (:horizontal, :vertical)
  • fontsize::Real: Font size in pixels
  • distance::Real: Label distance from node in pixels

Returns

  • Named tuple with dx_min, dx_max, dy_min, dy_max in pixels

Notes

  • Uses approximate character width of 0.6 × fontsize for typical fonts
  • Returns pixel-space estimates (caller must convert to data coordinates)
source
DAGMakie.compute_label_boundsFunction
compute_label_bounds(node_positions, nlabels, nlabels_align, nlabels_distance, nlabels_fontsize)

Compute the bounding box that encompasses all nodes and their labels.

Arguments

  • node_positions::AbstractVector: Vector of node positions (Point2f or similar)
  • nlabels::AbstractVector{<:AbstractString}: Vector of label strings
  • nlabels_align: Vector of alignment tuples or single tuple for all
  • nlabels_distance::Real: Label distance from node in pixels
  • nlabels_fontsize::Real: Font size for labels

Returns

  • Tuple (x_min, x_max, y_min, y_max) in data coordinates

Notes

  • Estimates pixel-to-data conversion based on current data range
  • Assumes typical figure width of ~500 pixels for the data range
source
DAGMakie.compute_padded_limitsFunction
compute_padded_limits(node_positions, nlabels, nlabels_align, nlabels_distance, nlabels_fontsize; padding=0.1, node_sizes=nothing)

Compute axis limits with padding that includes all nodes, labels, and marker extents.

Arguments

  • node_positions: Vector of node positions
  • nlabels: Vector of label strings (or nothing for no labels)
  • nlabels_align: Alignment specification
  • nlabels_distance: Label distance in pixels
  • nlabels_fontsize: Font size
  • padding::Float64 = 0.1: Padding as fraction of range (0.1 = 10%)
  • node_sizes: Optional node marker size(s) in pixels; used so flat chains/columns are not cropped under DataAspect

Returns

  • Tuple of tuples ((x_min, x_max), (y_min, y_max)) for axis limits
source
DAGMakie.get_node_positionsFunction
get_node_positions(p)

Extract node positions from a GraphPlot object.

Arguments

  • p: A GraphMakie GraphPlot object

Returns

  • Vector of node positions (Point2f or Point3f)
source
DAGMakie.graph_extentFunction
graph_extent(node_positions)

Compute the spatial extent of a graph from node positions.

Arguments

  • node_positions: Vector of node positions

Returns

  • Named tuple (x_min, x_max, y_min, y_max, x_range, y_range)
source
DAGMakie.compute_graph_layoutFunction
compute_graph_layout(g; kwargs...)

Resolve deterministic positions and feedback-edge routing metadata for a graph.

If layout is provided, those positions are respected, but the graph is still classified so cyclic feedback routing can be applied consistently.

When layout is omitted and the graph is a 3-node transitive triangle (unique source with edges to both other nodes plus an edge between them), an apex-top pedagogical layout is used so the shortcut edge remains visible.

source
DAGMakie.DAGLayoutResultType
DAGLayoutResult

Resolved node positions and routing metadata for a graph visualisation.

Fields

  • positions::Vector{Point2f}: Node positions in data coordinates
  • kind::Symbol: One of :acyclic, :cyclic, :mixed_acyclic, or :mixed_cyclic
  • node_layers::Vector{Int}: Layer index for each node
  • component_index::Vector{Int}: Strongly connected component index for each node
  • components::Vector{Vector{Int}}: Node membership for each component
  • component_layers::Vector{Int}: Layer index for each component
  • feedback_edges::Vector{Tuple{Int, Int}}: Directed edges routed as feedback edges
  • edge_waypoints::Dict{Tuple{Int, Int}, Vector{Point2f}}: Extra waypoints for curved edges
source
DAGMakie.classify_graph_kindFunction
classify_graph_kind(g)

Classify a graph by directed cyclicity and mixed-edge support.

Self-loops are ignored for this classification: they are drawn by GraphMakie as local self-arcs and should not force the multi-node cyclic / feedback layout.

source
DAGMakie.feedback_edge_maskFunction
feedback_edge_mask(g, layout_result)

Return a Boolean mask, aligned with edges(g), indicating which directed edges should be rendered as curved feedback overlays.

source

Themes and Styling

DAGMakie.dag_themeFunction
dag_theme()

Return a clean theme for DAG visualisation.

This theme removes axes, grids, and frames to create publication-ready causal diagrams with focus on the graph structure.

Returns

  • Theme: A Makie theme with clean DAG styling

Usage

using CairoMakie, DAGMakie

# Apply theme globally
set_theme!(dag_theme())

# Or use with_theme for a single plot
with_theme(dag_theme()) do
    dagplot(g, nlabels=["X", "Y", "Z"])
end
source
DAGMakie.apply_dag_theme!Function
apply_dag_theme!(ax)

Apply clean DAG theme to an existing axis.

Hides all axis decorations (spines, ticks, labels, grid) and sets DataAspect for proper graph proportions.

Arguments

  • ax: A Makie Axis object

Example

fig = Figure()
ax = Axis(fig[1, 1])
apply_dag_theme!(ax)
graphplot!(ax, g)
source
DAGMakie.DAGStyleType
DAGStyle

Preset styling configuration for DAG visualisation.

Fields

  • node_size::Real: Node size in pixels
  • node_color::Symbol: Default node colour
  • node_strokewidth::Real: Node outline width
  • node_strokecolor::Symbol: Node outline colour
  • edge_color::Symbol: Edge colour
  • edge_width::Real: Edge line width
  • arrow_size::Real: Arrowhead size
  • arrow_shift: Arrow position (:end or Float64)
  • label_fontsize::Real: Label font size
  • label_color::Symbol: Label colour
  • label_distance::Real: Label distance from node
  • padding::Float64: Padding fraction
source
DAGMakie.DEFAULT_SELFEDGE_SIZEConstant

Default GraphMakie selfedge_size (data units) for self-loops.

GraphMakie's automatic size is half the nearest-neighbour distance, which is oversized on typical DAG layouts; keep loops compact beside the marker.

source

Node Type Styling

DAGMakie.apply_node_type_stylingFunction
apply_node_type_styling(node_types::Vector{NodeType})

Generate styling arrays for a list of node types.

Arguments

  • node_types: Vector of NodeType for each node

Returns

  • Named tuple with:
    • colors: Vector of fill colours
    • markers: Vector of marker shapes
    • strokewidths: Vector of stroke widths
    • strokecolors: Vector of stroke colours
  • label_colors: Vector of in-node label colours
source
apply_node_type_styling(spec::DAGSpec)

Generate styling arrays from a DAGSpec.

source
DAGMakie.typed_confounding_graphFunction
typed_confounding_graph()

Create a confounding graph with proper node types.

Returns DAGSpec with: Confounder → Treatment → Outcome, Confounder → Outcome

source
DAGMakie.typed_mediation_graphFunction
typed_mediation_graph()

Create a mediation graph with proper node types.

Returns DAGSpec with: Treatment → Mediator → Outcome, Treatment → Outcome

source
DAGMakie.typed_instrumental_graphFunction
typed_instrumental_graph()

Create an instrumental variable graph with proper node types.

Returns DAGSpec with: Instrument → Treatment → Outcome, Confounder → Treatment, Confounder → Outcome

source

Mixed Graph Operations

DAGMakie.compute_bidirected_pathFunction
compute_bidirected_path(p1::Point2f, p2::Point2f; curvature=0.3)

Compute a curved path for a bidirected edge between two points.

Returns a vector of points representing a quadratic Bézier curve that arcs above the straight line connecting the two points.

Arguments

  • p1: Start point
  • p2: End point
  • curvature::Float64 = 0.3: How much the arc curves (fraction of distance)

Returns

  • Vector of Point2f representing the curved path
source
DAGMakie.compute_all_bidirected_pathsFunction
compute_all_bidirected_paths(mg::MixedGraph, positions::AbstractVector; curvature=0.3)

Compute curved paths for all bidirected edges in a mixed graph.

Arguments

  • mg: The mixed graph
  • positions: Vector of node positions
  • curvature: Arc curvature parameter

Returns

  • Vector of path vectors, one per bidirected edge
source
DAGMakie.bidirected_arrow_positionsFunction
bidirected_arrow_positions(mg::MixedGraph, positions::AbstractVector; curvature=0.3, arrow_offset=0.1)

Compute positions and rotations for arrowheads on bidirected edges.

Returns positions and rotation angles for arrows at both ends of each bidirected edge.

Arguments

  • mg: The mixed graph
  • positions: Vector of node positions
  • curvature: Arc curvature parameter
  • arrow_offset: How far from the node centre to place arrows (0-0.5)

Returns

  • Named tuple with:
    • positions: Vector of Point2f for arrow positions
    • rotations: Vector of Float64 rotation angles
    • edge_indices: Which bidirected edge each arrow belongs to
source

Utilities

DAGMakie.is_dagFunction
is_dag(g::AbstractGraph)

Check if a directed graph is acyclic (a valid DAG).

Arguments

  • g: A directed graph

Returns

  • true if the graph has no cycles, false otherwise
source
DAGMakie.edge_listFunction
edge_list(g::AbstractGraph)

Return a vector of (src, dst) tuples for all edges in the graph.

source
DAGMakie.adjacency_to_graphFunction
adjacency_to_graph(adj::AbstractMatrix)

Create a DiGraph from an adjacency matrix.

Arguments

  • adj: Square adjacency matrix where adj[i,j] != 0 indicates edge i → j

Returns

  • SimpleDiGraph with edges from the adjacency matrix
source
DAGMakie.graph_from_edgesFunction
graph_from_edges(n::Int, edge_pairs::Vector{Tuple{Int, Int}})

Create a DiGraph from a list of edge pairs.

Arguments

  • n: Number of vertices
  • edge_pairs: Vector of (src, dst) tuples

Returns

  • SimpleDiGraph with the specified edges

Example

g = graph_from_edges(3, [(1, 2), (2, 3), (1, 3)])
source