API Reference

Layout utilities

DAGMakie.time_indexed_layout — Function
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.dagplot_time_indexed — Function
dagplot_time_indexed(g, n_variables, n_times; kwargs...)

dagplot with time_indexed_layout for graphs unrolled over time.

Pass nlabels with one label per node in the same (t, variable) order as time_indexed_layout. When color_by is set, exposure / outcome / ancestor roles propagate across each variable row (see propagate_temporal_smart_roles). exposure and outcome may be node indices or (variable_index, time_index) tuples. Remaining keywords go to dagplot.

source
DAGMakie.temporal_layout — Function
temporal_layout(node_keys; onset_times, dx, dy, origin)

Compute positions for a temporal graph. node_keys contains (variable, time) pairs. A pointwise key (v, t::Integer) is placed at column t; a single-node key (v, nothing) is placed at its entry in onset_times (default 0). Rows follow the order in which variable names first occur. Placement is a display fact only: it does not encode temporal support, endurance, or ontology.

source
DAGMakie.dagplot_temporal — Function
dagplot_temporal(g, node_keys; onset_times,
    temporal_supports, value_representations, graph_kind, kwargs...)

Plot a temporal graph whose keys mix pointwise (v, t) nodes and single-node (v, nothing) keys placed at onset_times. Unlike dagplot_time_indexed, this API does not require a complete rectangular time grid. Markers follow value_representations (see marker_for_value_representation); temporal_supports is accepted for callers that carry it but does not change the glyph.

source
DAGMakie.interval_summary_node_marker — Function
interval_summary_node_marker()

Return the Makie marker used when value_representation = :interval_summary under the package visual convention. Labels or support bars identify the interval summarised; shape alone does not establish support or ontology.

source
DAGMakie.marker_for_value_representation — Function
marker_for_value_representation(repr)

Glyph from declared value representation. Only :interval_summary selects the rounded rectangle under the package convention. Attributes, trajectories, states, and events use a circle. Temporal support and referent identity never select the shape.

source
DAGMakie.digraph_skeleton — Function
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_matrix — Function
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_labels — Function
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;
    labels = 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
DAGMakie.edge_terminal_markers — Function
edge_terminal_markers(g, weights; atol=0.0)

Return GraphMakie arrow markers in Graphs.edges(g) order. Negative entries in weights use INHIBITORY_EDGE_MARKER, producing an inhibitory -| terminal; non-negative entries use the ordinary arrowhead.

weights may be a vector with one entry per edge, or a square structural matrix B whose B[i, j] entry is the weight on j → i. This is display metadata only: DAGMakie does not infer a mechanism sign from graph topology. Values with absolute magnitude at most atol use an ordinary arrowhead.

source

Main Plotting Functions

DAGMakie.dagplot — Function
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 preferred kwargs as dagplot / dagplot! (labels, label_position, color_by, …)

Returns

  • Tuple (fig, ax, p)
source
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
  • Preferred plot kwargs (full list on dagplot!): labels, label_position (:inner / :outer), color_by, exposure, outcome, label_obstacle_graph, plus GraphMakie pass-throughs (nlabels_*, elabels_*, …).

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; labels=["X", "Y", "Z"])
save("dag.png", fig)

# Outer labels
fig, ax, p = dagplot(g; labels=["X", "Y", "Z"], label_position=:outer)

# Access node positions
positions = p[:node_pos][]
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, labels=["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, labels=["Z", "X", "Y"])
source
DAGMakie.dagplot! — Function
dagplot!(ax, spec::DAGSpec; kwargs...)

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

Kwargs match dagplot! on AbstractGraph.

source
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_mode = :auto: :auto uses a deterministic layered layout for DAGs and an SCC-aware layout for cyclic digraphs; :acyclic / :cyclic force those modes; :spring uses NetworkLayout Spring(). Pass explicit layout = … (positions or a NetworkLayout algorithm) to bypass the strategy
  • padding::Float64 = 0.1: Padding around graph as fraction of range
  • layer_gap = 2.6: Spacing between layered columns (or rows)
  • node_gap = nothing: Within-layer spacing. Defaults to DEFAULT_NODE_GAP_INNER (2.6) when label_position=:inner, and DEFAULT_NODE_GAP_OUTER (1.8) when label_position=:outer
  • Comparison / intervention helpers reuse a shared DAGLayoutResult so panels keep the same node positions

Node Keyword Arguments

  • node_size = DEFAULT_NODE_SIZE: Node marker size in pixels (theme default 34; outer labels use OUTER_LABEL_NODE_SIZE unless set explicitly)
  • 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
  • straight_edges = nothing: Shorthand for edge_routing = Dict(e => :straight for e in edges)
  • edge_routing = nothing: Dict((src, dst) => spec) per-edge routing — straight by default; curve with :curved, CurvedEdge (default bow DEFAULT_EDGE_BOW), a bow fraction (Real), or CurvedEdge(distance=…) for GraphMakie bend angle $γ = 2\operatorname{atan}(2d/L)$
  • long_edge_routing = :quadratic: Geometry for bowed edges (:none, :natural_cubic, …)
  • arrow_size = 10: Arrowhead size
  • arrow_shift = :end: Arrow position (:end or Float64 0-1)
  • edge_weights = nothing: Optional signed edge values. A negative value uses an inhibitory -| terminal; pass either one value per Graphs.edges(g) entry or a structural matrix B with B[i, j] the value on j → i.
  • 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

  • labels = nothing: Node labels (preferred DAGMakie name). nlabels= is the GraphMakie-compatible alias
  • 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.
  • label_position = :outer: :outer (default) places labels outside in the largest angular gap (compact OUTER_LABEL_NODE_SIZE markers, dark text); :inner centres labels in the node fill (white text; pass fit_node_size_to_labels=true for oval markers on long names) label_position = :outer. true still enables outer placement when label_position is left at :inner
  • label_obstacle_graph = nothing: Optional graph used only for outer-label angle gaps (defaults to the plotted graph). Intervention plots pass the factual DAG so grey removed parent edges still count as obstacles.
  • fit_node_size_to_labels = false: When true and label_position=:inner, size in-node markers from each label (short labels stay round; wider labels become ovals). Skipped for outer labels or an explicit node_size. Pass true with label_position=:inner for fitted in-node markers.
  • 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 label_position = :outer for outside labels)
  • nlabels_fontsize = 16: Label font size
  • nlabels_color = :white: Label colour (white on dark node fills; dark via OUTER_LABEL_COLOR when label_position = :outer)

Colouring / dagitty roles

  • color_by = nothing: Set :ancestors (or true) for dagitty-style ancestor colours, or :adjustment to also emphasise a backdoor adjustment set (needs CausalInference or adjustment=). Deprecated alias: smart
  • exposure, outcome: Exposure and outcome node indices (required when color_by is on). - adjustment: Optional Set{Int} for color_by=: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, labels=["Z", "X", "Y"])

# With node colours indicating roles
dagplot!(ax, g,
    labels=["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, labels=["A", "B", "C"])
dagplot!(ax2, g2, labels=["X", "Y", "Z"])
dagplot!(ax3, g3, labels=["P", "Q", "R"])
source
dagplot!(ax, mg::MixedGraph; kwargs...)

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

Accepts the same preferred label kwargs as dagplot! on AbstractGraph (labels, label_position, …), plus bidirected-edge styling (bidirected_color, bidirected_width, …).

source

Convenience Pattern Functions

DAGMakie.dagplot_chain — Function
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_fork — Function
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_collider — Function
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_confounding — Function
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_mediation — Function
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_confounded — Function
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_frontdoor — Function
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_confounded — Function
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_bias — Function
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.NodeType — Type
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.NodeSpec — Type
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.EdgeType — Type
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.EdgeSpec — Type
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.DAGSpec — Type
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.MixedGraph — Type
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.CausalPath — Type
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.PathSegment — Type
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_edges — Function
path_edges(path::CausalPath)

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

source

Interventions

DAGMakie.Intervention — Type
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.CausalQuery — Type
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_graph — Function
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_spec — Function
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.HighlightSpec — Type
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

Precedence

When the same node or edge is listed more than once, the first listing wins (exposure / outcome / adjustment colours should therefore precede path colours in merged specs). Default theme colours sit underneath.

source
DAGMakie.highlight_from_paths — Function
highlight_from_paths(paths::Vector{CausalPath}; colors=nothing)

Create a HighlightSpec from multiple paths with different colors.

source
DAGMakie.highlight_adjustment_set — Function
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_paths — Function
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_highlighted — Function
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, labels=["Z", "X", "Y"])
source
DAGMakie.dagplot_highlighted! — Function
dagplot_highlighted!(ax, g::AbstractGraph, highlight::HighlightSpec; kwargs...)

Plot a highlighted DAG into an existing axis.

Prefer labels= and label_position= (see dagplot!). Alias nlabels= / auto_align_labels= still work.

source
DAGMakie.dagplot_backdoor — Function
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_dsep — Function
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_adjustment — Function
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_surgery — Function
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; kwargs...)

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_intervention — Function
dagplot_intervention(g::AbstractGraph, intervention::Intervention;
                     show_removed_edges::Bool=true, kwargs...)

Plot a DAG with intervention applied, showing removed edges.

Arguments

  • g: Original DAG
  • intervention: Intervention specification
  • show_removed_edges: If true, overlay severed parent edges as dashed grey (deprecated alias: show_original)
  • 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, labels=labels)
source
DAGMakie.dagplot_intervention! — Function
dagplot_intervention!(ax, g::AbstractGraph, intervention::Intervention; kwargs...)

Plot intervention into an existing axis.

Preferred kwargs (see also dagplot!): labels, label_position, show_removed_edges (alias show_original), do_node_labels (alias relabel_nodes). Outer labels use the factual DAG as label_obstacle_graph when removed edges are overlaid.

source
DAGMakie.dagplot_do — Function
dagplot_do(g::AbstractGraph, intervention_node::Int; labels=nothing, kwargs...)

Convenience function for single-node intervention visualisation.

Prefer labels= (alias nlabels=). Other kwargs match dagplot_intervention / dagplot!.

source
DAGMakie.dagplot_comparison — Function
dagplot_comparison(g::AbstractGraph, intervention::Intervention;
                   labels=nothing, kwargs...)

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

Prefer labels= (alias nlabels=). Shared layout and remaining kwargs are passed to dagplot! / dagplot_intervention!.

source
DAGMakie.query_to_string — Function
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_edge — Function
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 (DAGMakie.vaccine_nutrition_idag_spec) when the claim is about effect variation.

source
DAGMakie.dagplot_side_by_side — Function
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.did_2x2_factual_spec — Function
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_spec — Function
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.SmartNodeRole — Type
SmartNodeRole

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

source
DAGMakie.classify_smart_roles — Function
classify_smart_roles(g, exposure, outcome; anc_treatment=nothing, anc_outcome=nothing)

Classify each vertex into a SmartNodeRole given exposure and outcome (1-based indices). Argument names treatment / anc_treatment remain for compatibility with older call sites.

source
DAGMakie.smart_style_for_graph — Function
smart_style_for_graph(g, exposure, outcome; mode=:ancestors, adjustment=nothing)

Return a named tuple of per-node styling for dagitty-like colouring (color_by modes).

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.apply_smart_kwargs — Function
apply_smart_kwargs(g; color_by, exposure, outcome, adjustment, kwargs...)

If color_by is enabled, merge dagitty-style colours into keyword args for dagplot!. Explicit node_color from the caller wins.

Deprecated aliases: smart= for color_by=, treatment= for exposure=.

source
DAGMakie.dagplot_smart — Function
dagplot_smart(g, treatment, outcome; color_by=:ancestors, kwargs...)

Convenience wrapper: dagplot with dagitty-style ancestor colours. Prefer color_by=; smart= remains an alias.

source
DAGMakie.propagate_temporal_smart_roles — Function
propagate_temporal_smart_roles(
    roles, n_variables, n_times; exposure, outcome
) -> Vector{SmartNodeRole}

Propagate per-node SmartNodeRoles across each variable row so Treatment[t] / Outcome[t] styling applies at every occasion on the estimand path (see issue #5).

  • The exposure variable row → all SmartExposure
  • The outcome variable row → all SmartOutcome
  • Other rows → strongest ancestral role among occasions (both > X-only > Y-only > gray)
source
DAGMakie.temporal_role_styling — Function
temporal_role_styling(
    g, n_variables, n_times; exposure, outcome, color_by=:ancestors, adjustment=nothing
) -> NamedTuple

Convenience wrapper returning (node_color, nlabels_color, node_strokewidth, node_strokecolor, roles, adjustment) for manual dagplot / dagplot_time_indexed calls (#5).

exposure and outcome may be node indices or (variable_index, time_index) pairs.

source
DAGMakie.ancestor_sets — Function
ancestor_sets(g, exposure, outcome) -> (anc_exposure, anc_outcome)

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

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

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

source

Preferred keyword names

Public plotting prefers discoverable names; legacy aliases still work:

PreferAlias (removed from public API)
labelsnlabels (GraphMakie pass-through still accepted)
label_position = :outerformer auto_align_labels=true
label_obstacle_graphformer auto_align_graph
color_byformer smart
exposureformer treatment (colouring kwargs)
show_removed_edgesformer show_original
do_node_labelsformer relabel_nodes
OUTER_LABEL_*former AUTO_ALIGN_*

GraphMakie pass-throughs keep the nlabels_* / elabels_* prefixes.

Label Alignment

DAGMakie.compute_auto_label_aligns — Function
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 DAGMakie.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_direction — Function
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

Layout Utilities

DAGMakie.estimate_label_extent — Function
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.node_size_for_inner_label — Function
node_size_for_inner_label(label; fontsize, padding, min_size, marker=:auto)

Choose a GraphMakie node_size (and marker) that encloses an in-node label.

Short labels keep a circle whose diameter covers the text; wider labels keep FIT_NODE_MARKER (Makie.Circle) with a (width, height) size (an oval / ellipse). Pass marker = :circle or Circle to force a round disk; pass marker = :rect to force a rectangular box.

Returns

  • (size, marker) where size is a Real or (width, height) tuple
source
DAGMakie.fit_node_sizes_to_labels — Function
fit_node_sizes_to_labels(nlabels; fontsize, padding, min_size, markers=nothing)

Fit a size (and marker) for each in-node label.

When markers === nothing, markers are FIT_NODE_MARKER (round or oval). When markers is a vector, sizes respect those shapes (:circle is normalised to FIT_NODE_MARKER so sizing matches the drawn glyph).

source
DAGMakie.compute_label_bounds — Function
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_limits — Function
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_positions — Function
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_extent — Function
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_layout — Function
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.DAGLayoutResult — Type
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
  • edge_curve_distances::Dict{Tuple{Int, Int}, Float64}: GraphMakie curve_distance per edge
source
DAGMakie.classify_graph_kind — Function
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_mask — Function
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
DAGMakie.count_layered_crossings — Function
count_layered_crossings(g, layers)

Count pairwise edge crossings between consecutive layers of a layered digraph. Used for layout quality checks; not a public drawing API.

source

Edge routing

Edges are straight by default. Per-edge bows use edge_routing on dagplot / dagplot! (see Basic Plotting and Output Quality).

DAGMakie.CurvedEdge — Type
CurvedEdge

Per-edge bow for dagplot. Unspecified edges are straight.

Fields

  • bow::Union{Real, Nothing}: Lateral quadratic control offset as a fraction of chord length (typical 0.12–0.35). Defaults to DEFAULT_EDGE_BOW. Ignored when distance is set.
  • distance::Union{Real, Nothing}: GraphMakie curve_distance in data units (perpendicular sagitta). The bend angle along the chord satisfies $γ = 2 \operatorname{atan}(2d / L)$ for chord length $L$.
  • side::Symbol: :auto bows away from the nearest non-endpoint node on the chord; :left / :right are relative to the forward chord direction.

Examples

edge_routing = Dict(
    (1, 4) => CurvedEdge(),              # default bow
    (1, 5) => CurvedEdge(bow = 0.28),    # deeper arc
    (2, 4) => CurvedEdge(distance = 0.35),
    (3, 5) => 0.18,                      # bow fraction shorthand
)
source

Themes and Styling

DAGMakie.dag_theme — Function
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, labels=["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.DAGStyle — Type
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.FIT_NODE_MARKER — Type

Marker used when fitting node size to in-node labels.

Prefer geometry Makie.Circle over the :circle BezierPath: Makie draws :circle at roughly 70% of markersize, so label-based sizes look too small and text overflows. Circle matches markersize one-to-one (including ovals via (width, height)).

source
DAGMakie.DEFAULT_SELFEDGE_SIZE — Constant

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_styling — Function
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_graph — Function
typed_confounding_graph()

Create a confounding graph with proper node types.

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

source
DAGMakie.typed_mediation_graph — Function
typed_mediation_graph()

Create a mediation graph with proper node types.

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

source
DAGMakie.typed_instrumental_graph — Function
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_path — Function
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_paths — Function
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_positions — Function
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_dag — Function
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_list — Function
edge_list(g::AbstractGraph)

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

source
DAGMakie.adjacency_to_graph — Function
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_edges — Function
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