25  Julia Packages Used in This Book

This book relies on a small Julia causal econometrics stack developed alongside the examples. The packages live under ~/projects/software and are loaded through this book’s local Julia environment. The goal is not to replace the broader Julia ecosystem, but to fill the gaps that matter for the workflows in this text: fixed effects, semiparametric causal estimators, staggered DiD, synthetic control, TASC, regression discontinuity, SEM, and causal mediation.

The packages are regular Julia projects. From this book directory, the manifest points to them through paths such as ../../software/Panelest.jl, which resolves to ~/projects/software/Panelest.jl.

25.1 Working With The Environment

The reproducible workflow is:

cd ~/projects/books/causal_econometrics_julia
julia --project -e 'using Pkg; Pkg.instantiate()'
julia --project -e 'using CausalGraphs, CausalEstimate, Panelest, DiD, SynthDiD, RDRobust, Lavaan, Crumble'
quarto render

The standard julia-1.12 Jupyter kernel uses --project=@., so Quarto picks up the book environment when rendering from this directory. A custom sysimage can still be useful for speed, but it is optional and should be rebuilt whenever the local packages change.

25.2 Package Map

Package Used For Main Book Chapters
Panelest.jl Fixed-effects OLS, GLM-style panel models, clustered covariance, and IV/2SLS support Estimation, DiD, IV/RDD
CausalGraphs.jl DAG/ADMG construction, graph-based identification, Pearl-Shpitser ID, and estimator routing Identification, DAGs/ADMGs, Smoking cessation
CausalEstimate.jl Unified TMLE and AIPW estimation: estimate(ATE/ATT(...), TMLE/AIPW(...), df) and graph-implied backdoor via GraphID Estimation, Nonparametric, Smoking cessation
Panelest.jl Also exports emfx() and dataset() for ETWFE post-processing DiD
SynthDiD.jl Synthetic control, standard DiD, and synthetic DiD estimators DiD
TASC.jl Time-Aware Synthetic Control with Kalman filtering, RTS smoothing, EM fitting, SC/RSC utilities, and TASC plot recipes Synthetic control, DiD, and TASC
RDRobust.jl Local-polynomial RD estimation, bandwidth selection, RD plot data, and fuzzy RD IV/RDD
Lavaan.jl SEM, CFA, defined parameters, fit measures, and lavaan-style mediation syntax Mediation
Crumble.jl Flexible causal mediation using cross-fitting, Riesz representers, and neural-network nuisance estimation Mediation

25.3 Panelest.jl

Panelest.jl is the workhorse for regression models with high-dimensional fixed effects. Its API is intentionally close to the formula style used throughout the book:

using Panelest, Vcov

feols(df, @formula(y ~ x + fe(id) + fe(year)))
fepois(df, @formula(y ~ x + fe(id) + fe(year)))
feols(df, @formula(y ~ x + fe(id) + fe(year)), vcov = Vcov.cluster(:id))

The package exports feols, fepois, felogit, feprobit, feglm, clogit, cre, feiv, and fe. The fixed-effect term fe(...) is the central convenience feature. In the DiD chapter, it lets ETWFE specifications stay readable:

feols(
    mpdta,
    @formula(lemp ~ lpop + first_treat_str & year_str + fe(countyreal) + fe(year)),
    vcov = Vcov.cluster(:countyreal),
)

The current package also includes feiv for 2SLS-style workflows:

feiv(df, @formula(y ~ x_exo + fe(id)), endo = :x_endo, inst = :z)

25.4 CausalEstimate.jl

CausalEstimate.jl is the unified estimation front-end used in the estimation, nonparametric, and applied graph chapters. It combines TMLE and AIPW under one estimate(...) call, with cross-fitting and doubly robust inference built in.

using CausalEstimate, DataFrames

n = 1000
df = DataFrame(
    Y = randn(n),
    A = rand([0, 1], n),
    W = randn(n),
)

# AIPW for ATE
result = estimate(ATE(outcome=:Y, treatment=:A, confounders=[:W]), AIPW(crossfit=5), df)
estimate(result)       # point estimate
confint(result)        # (lower, upper) 95% CI
pvalue(result)         # two-sided p-value

# TMLE for ATE
result_tmle = estimate(ATE(outcome=:Y, treatment=:A, confounders=[:W]), TMLE(crossfit=5), df)

# ATT
result_att = estimate(ATT(outcome=:Y, treatment=:A, confounders=[:W]), AIPW(crossfit=5), df)

For graph-identified backdoor (a-fixable) effects, pass a GraphID layer:

using CausalEstimate, CausalGraphs

g = make_graph(
    vertices = [:X, :A, :Y],
    di_edges = [(:X, :A), (:X, :Y), (:A, :Y)],
)

result = estimate(
    ATE(outcome = :Y, treatment = :A),
    GraphID(graph = g),
    AIPW(crossfit = 5),
    df,
)

GraphID reads the Markov pillow of the treatment from the graph and uses it as the adjustment set. For p-fixable (front-door), nested-fixable, and general ID-algorithm effects, use CausalGraphs.estimate_causal(...) directly.

25.5 CausalGraphs.jl

CausalGraphs.jl is the graph layer for the DAG/ADMG chapters. It constructs graphs, checks graph properties, runs identify(), and exposes the general Pearl-Shpitser ID_algorithm():

using CausalGraphs

g = make_graph(
    vertices = [:X, :A, :Y],
    di_edges = [(:X, :A), (:X, :Y), (:A, :Y)],
)

identify(g, :A, :Y).strategy

For non-backdoor effects (p-fixable, nested-fixable, ID plug-in), CausalGraphs.estimate_causal(...) provides the full estimation pipeline:

res = CausalGraphs.estimate_causal(
    a = [1.0, 0.0],
    data = df,
    graph = g,
    treatment = :A,
    outcome = :Y,
)

res[:TMLE].ACE         # front-door NPS TMLE
res[:IDPlugin].ACE     # finite-support ID plug-in

25.6 ETWFE helpers in Panelest

Panelest also exports emfx() and dataset() for ETWFE post-processing:

using Panelest

att = emfx(model)
event_study = emfx(model, type = "event")
calendar    = emfx(model, type = "calendar")

emfx() reads the cohort-by-time interaction coefficients from a feols model and aggregates them into an overall ATT, event-time effects, or calendar-time effects. dataset("mpdta") returns a synthetic balanced panel with string cohort and year columns ready for StatsModels ETWFE interactions.

25.7 SynthDiD.jl

SynthDiD.jl implements the comparison among standard DiD, synthetic control, and synthetic DiD. The package exposes a matrix-oriented workflow:

using SynthDiD

df = california_prop99()
setup = panel_matrices(df, :State, :Year, :PacksPerCapita, :treated)

τ_sdid = synthdid_estimate(setup.Y, setup.N0, setup.T0)
τ_sc   = sc_estimate(setup.Y, setup.N0, setup.T0)
τ_did  = did_estimate(setup.Y, setup.N0, setup.T0)

The most important functions are panel_matrices, synthdid_estimate, sc_estimate, did_estimate, effect_curve, placebo, vcov, se, california_prop99, and synthdid_controls.

25.8 TASC.jl

TASC.jl implements Time-Aware Synthetic Control. It keeps the same panel-data spirit as synthetic control, but models the untreated outcome panel with a low-rank linear Gaussian state-space model:

include("../../software/TASC.jl/src/TASC.jl")
using .TASC

model = fit_tasc(Y; d = 2, T0 = 19)
pred = predict_counterfactual(model, Y)

The package exports fit_tasc, predict_counterfactual, predict_post_intervention, tasc_plot, classical synthetic-control baselines, HSVT/RSC-style denoising helpers, and synthetic data generators. In the synthetic-control chapter, the package is included from source so that the new local package can be used without forcing a full resolver update of the book environment.

25.9 RDRobust.jl

RDRobust.jl is the RD layer used in the IV/RDD chapter. It mirrors the familiar rdrobust workflow:

using RDRobust

fit = rdrobust(y, x; c = 0.0)
bw = rdbwselect(y, x; c = 0.0)
plot_data = rdplot(y, x; c = 0.0)

It supports local-polynomial RD estimation, robust bias-corrected intervals, bandwidth selectors, covariate adjustment, clustered inference, multiple kernels, and fuzzy RD designs. In the book, rdrobust is used both for a sharp RD example and for a fuzzy RD comparison with a manual 2SLS specification.

25.10 Lavaan.jl

Lavaan.jl ports the R lavaan modeling style to Julia. It is used for classical SEM-based mediation:

using Lavaan

model = """
  m ~ a * x
  y ~ b * m + c * x
  indirect := a * b
  total := c + indirect
"""

fit = sem(model, df)
parameterEstimates(fit)

The public API includes lavaan, cfa, sem, growth, sam, parameterEstimates, fitMeasures, standardizedSolution, lavInspect, lavTestLRT, modindices, rsquare, lavPredict, and simulateData. The package supports the familiar lavaan operators =~, ~, ~~, ~1, and :=.

25.11 Crumble.jl

Crumble.jl is the causal mediation package used for interventional and recanting-twin estimands. Its entry point is crumble():

using Crumble

control = crumble_control(crossfit_folds = 5, epochs = 50)

result = crumble(
    df,
    ["A"];
    outcome = "Y",
    mediators = ["M"],
    moc = ["Z"],
    covar = ["W1", "W2"],
    effect = "RT",
    control = control,
)

result.estimates

The package exports crumble, crumble_control, sequential_module, and CrumbleResult. The supported effect families are natural ("N"), organic ("O"), randomized interventional ("RI"), and recanting twin ("RT"). In the mediation chapter, "RT" is used because it handles a mediator-outcome confounder affected by treatment.

25.12 Practical Advice

These packages are best treated as a coherent local research stack. When changing package code, rerun a direct load check from the book directory:

julia --project -e 'using CausalEstimate, CausalGraphs, Panelest, SynthDiD, RDRobust, Lavaan, Crumble'

For content changes, render the affected chapter first. For package API changes, render the full book after deleting the relevant _freeze chapter directory so Quarto does not reuse stale cached results.