[ANN] CausalStructures.jl: a Julia package for causal graphs

I’d like to share CausalStructures.jl, a package for causal graphs that I’ve been working on the last several months, and I just released 1.0.

CausalStructures.jl supports DAGs, of course, but also their equivalence classes (PDAGs/MPDAGs/CPDAGs), graphs with latent variables (ADMGs), Ancestral Graphs (AGs/MAGs), and equivalence classes of MAGs (PAGs). You can then work with the causal graphs, such as:

  • Queries: d-/m-separation, ancestors/descendants, Markov blankets, etc.
  • Identification: the Generalized Adjustment Criterion, backdoor, frontdoor, instrumental variables, the ID/IDC algorithm, etc.
  • Transformations: moralization, latent projection, DAG from/to MPDAG/CPDAG, MAG from/to PAG, etc.

Example

To define a DAG we write the edges as a string
(+ fans a marker out to several nodes at once):

using CausalStructures

dag = DAG("Z --> X + Y, X --> M --> Y")

We can then e.g. find an adjustment set that identifies the effect of X on Y:

adjustment_set(dag, :X, :Y; type = :optimal)
#> 1-element Vector{Symbol}:
#>  :Z

Or convert the DAG to its Markov equivalence class:

dag_to_cpdag(dag)
#> CPDAG with 4 nodes and 4 edges:
#>  nodes: M, X, Y, Z
#>  edges:
#>   M --- X, X --- Z, M --> Y, Z --> Y

Of course this barely scratches the surface of what the package can do; see the docs for the rest.

Plotting

Plotting requires loading a Makie backend, and a layout - it then works on any graph class natively. Styling is fully customizable, e.g. node/edge colors, shapes, curvature, and labels:

using CausalStructures, CairoMakie, NetworkLayout

pag = PAG(
    "C o-> X, D --> G + Y, X --> D + F, Y --> H, K o-> X, K --> Y"
)

plot(
    pag;
    layout           = :spring,
    node_color       = Dict(:X => :skyblue, :Y => :gold, :default => :whitesmoke),
    node_strokecolor = Dict(:X => :royalblue, :Y => :darkorange, :default => :slategray),
    node_shape       = Dict(:K => :square, :default => :circle),
    node_linestyle   = Dict(:K => :dash, :default => nothing),
    edge_color       = Dict(:partially_directed => :royalblue, :default => :darkslategray),
    edge_linestyle   = Dict(:partially_directed => :dash),
    curvature        = Dict((:K, :Y) => 0.3),
    edge_labels      = Dict((:K, :X) => "cool"),
    node_label_color = Dict(:X => :navy, :Y => :saddlebrown, :default => :black),
    title            = "A cool PAG",
    title_fontsize   = 18,
    title_color      = :navy,
)

See the plotting documentation for more details.

Related packages

  • dagitty:
    Very similar to what this package tries to do. However, it mainly focuses on DAGs only.
  • CausalInference.jl:
    Mainly focuses on causal discovery, and not working with causal graphs. It does implement a few algorithms for DAGs only.

Performance

Performance has also been a priority; see the benchmark docs
for numbers on individual functions, and /benchmark for a direct comparison against CausalInference.jl on the overlapping functionality.

CausalStructures.jl is also --trim=safe compatible for those that care about that :).

Feedback, issues, and contributions are all very welcome:

5 Likes