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 renderThe 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).strategyFor 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-in25.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.estimatesThe 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.