You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️Work in Progress — This package is under active development and not yet registered.
Publication-ready visualisation of Directed Acyclic Graphs (DAGs) for causal inference.
DAGMakie provides clean, minimal DAG visualisation with sensible defaults for academic papers and presentations. It builds on GraphMakie.jl with features specifically designed for causal diagrams.
Features
Automatic label alignment: Labels positioned to avoid edge overlaps
Publication-ready themes: Clean styling with no axes or grids
Causal diagram conventions: Support for observed, latent, treatment, and outcome nodes
Common patterns: Convenience functions for chain, fork, collider, confounding DAGs
Style presets: Default, minimal, bold, and presentation styles
Installation
Since this package is not yet registered, install from the local path:
using Pkg
Pkg.develop(path="/path/to/DAGMakie.jl")
Quick Start
using Graphs, DAGMakie, CairoMakie
# Create a simple confounding DAG: Z → X → Y, Z → Y
g =SimpleDiGraph(3)
add_edge!(g, 1, 2) # Z → Xadd_edge!(g, 1, 3) # Z → Yadd_edge!(g, 2, 3) # X → Y# Plot with labels
fig, ax, p =dagplot(g, nlabels=["Z", "X", "Y"])
save("confounding_dag.png", fig)
# Common causal patterns with one function call
fig, ax, p =dagplot_chain(["X₁", "X₂", "X₃"])
fig, ax, p =dagplot_fork(["Effect₁", "Cause", "Effect₂"])
fig, ax, p =dagplot_collider(["Cause₁", "Effect", "Cause₂"])
fig, ax, p =dagplot_confounding(["Confounder", "Treatment", "Outcome"])
fig, ax, p =dagplot_mediation(["Treatment", "Mediator", "Outcome"])
using DAGMakie, CairoMakie
# Create a mixed graph with bidirected edge (X ↔ Y = unmeasured confounder)
mg =mixed_graph(3,
[(1, 2), (2, 3)], # Directed: Z → X → Y
[(2, 3)] # Bidirected: X ↔ Y
)
fig, ax, p =dagplot(mg, nlabels=["Z", "X", "Y"])
# Or use convenience function
fig, ax, p =dagplot_iv_confounded(["Instrument", "Treatment", "Outcome"])
# Customise bidirected edge appearance
fig, ax, p =dagplot(mg,
nlabels = ["Z", "X", "Y"],
bidirected_color =:red,
bidirected_style =:dot,
bidirected_curvature =0.4
)
d-Separation and Backdoor Paths
# Create a confounded DAG: Z → X → Y, Z → Y
g, labels =confounding_graph(["Z", "X", "Y"])
# Test d-separationis_d_separated(g, 2, 3, Set{Int}()) # false - X and Y connected via Zis_d_separated(g, 2, 3, Set([1])) # true - conditioning on Z blocks the path# Find backdoor paths from X to Y
backdoor =find_backdoor_paths(g, 2, 3)
# Check if adjustment set is validis_valid_adjustment_set(g, 2, 3, Set([1])) # true - Z blocks backdoor# Find minimal adjustment set
adj =find_minimal_adjustment_set(g, 2, 3) # returns Set([1])
Path Highlighting Visualisation
# Visualise backdoor paths (open paths in red, blocked in grey)
fig, ax, p =dagplot_backdoor(g, 2, 3, nlabels=labels)
# With adjustment set - shows paths are blocked
fig, ax, p =dagplot_backdoor(g, 2, 3,
adjustment =Set([1]),
nlabels = labels
)
# Visualise d-separation status
fig, ax, p =dagplot_dsep(g, 1, 3, Set([2]), nlabels=labels)
# Show causal (directed) paths
fig, ax, p =dagplot_causal_paths(g, 2, 3, nlabels=labels)
# Auto-compute and show adjustment set
fig, ax, p =dagplot_adjustment(g, 2, 3, nlabels=labels)
Interventions (do-operator)
# Graph surgery: remove incoming edges to intervention target
g, labels =confounding_graph(["Z", "X", "Y"])
g_do =do_surgery(g, 2) # do(X) - removes Z → X# Visualise intervention (shows removed edges as dashed)
int =Intervention(2; label="do(X)")
fig, ax, p =dagplot_intervention(g, int, nlabels=labels)
# Convenience function
fig, ax, p =dagplot_do(g, 2, nlabels=labels)
# Side-by-side comparison: original vs post-intervention
fig =dagplot_do_comparison(g, 2, nlabels=labels)
# Check if causal effect is identifiablecausal_effect_identifiable(g, 2, 3) # true if backdoor criterion satisfied# Check if intervention removes confoundingintervention_removes_confounding(g, 2, 3) # true# Identify confounders
confounders =identify_confounders(g, 2, 3) # returns [1] (Z)
CausalDynamics.jl Integration
using DAGMakie, CausalDynamics, CairoMakie, Graphs
# Create an SCM
g =DiGraph(3)
add_edge!(g, 1, 2) # Z → Xadd_edge!(g, 2, 3) # X → Yadd_edge!(g, 1, 3) # Z → Y
scm =GraphSCM(g, Dict{Int,Function}(), Set{Int}())
# Plot the SCM directly
fig, ax, p =dagplot(scm, nlabels=["Z", "X", "Y"])
# Use CausalDynamics' d-separation
CausalDynamics.d_separated(g, 2, 3, [1]) # true# Get the extension module for advanced functions
ext = Base.get_extension(DAGMakie, :DAGMakieCausalDynamicsExt)
# Comprehensive causal analysis
analysis = ext.causal_analysis(g, 2, 3)
# Returns: (backdoor_paths, adjustment_set, identifiable, d_separated_unconditional)# Print formatted analysis report
ext.print_causal_analysis(g, 2, 3, nlabels=["Z", "X", "Y"])
# Plot with CausalDynamics-computed adjustment
fig, ax, p = ext.dagplot_adjustment_cd(g, 2, 3, nlabels=["Z", "X", "Y"])
# Visualise SCM with exogenous variables highlighted
scm_with_exog =GraphSCM(g, Dict{Int,Function}(), Set([1])) # Z is exogenous
fig, ax, p = ext.dagplot_scm(scm_with_exog, nlabels=["Z", "X", "Y"], show_exogenous=true)
Style Presets
# Different styles for different contexts
style =default_style() # Standard academic paper
style =minimal_style() # Thin lines, small nodes
style =bold_style() # Thick lines, large nodes
style =presentation_style() # Extra large for slides
API Reference
Main Functions
Function
Description
dagplot(g; kwargs...)
Create new figure with DAG
dagplot!(ax, g; kwargs...)
Plot DAG into existing axis
dagplot(spec::DAGSpec; kwargs...)
Plot from specification
Keyword Arguments
Layout
layout = Spring() — Layout algorithm from NetworkLayout.jl
padding = 0.1 — Padding around graph (fraction of range)
Nodes
node_size = 12 — Node size in pixels
node_color = :lightblue — Fill colour
node_strokewidth = 1.0 — Outline width
node_strokecolor = :black — Outline colour
Edges
edge_color = :black — Edge colour
edge_width = 1.0 — Edge line width
arrow_size = 10 — Arrowhead size
arrow_shift = :end — Arrow position
Labels
nlabels = nothing — Node labels (vector of strings)
If you use DAGMakie.jl in your research, please cite:
@software{dagmakie2026,
author = {Babayan, Simon A.},
title = {DAGMakie.jl: Publication-ready DAG visualisation for Julia},
year = {2026},
url = {https://github.com/SimonAB/DAGMakie.jl}
}
About
Publication-ready DAG visualisations for causal inference in Julia, built on GraphMakie.jl