StatsPAI — Ecosystem & Code Statistics¶
This page tracks StatsPAI's size and coverage against the broader statistical ecosystem, in the spirit of evidence-based positioning. Numbers marked "measured" are reproducible on a standard install; numbers marked "estimated" are extrapolated from public ecosystem statistics with the reasoning shown.
StatsPAI live numbers last measured: 2026-10-07 against the local
statspai1.38.0 source tree (python scripts/registry_stats.py; line counts do not depend on the platform). External ecosystem comparison rows are retained from the 2026-05-03 measurement unless noted otherwise.The four StatsPAI numbers (function count, submodule count, source LOC, test LOC) are reproducible from a single canonical generator:
python scripts/registry_stats.py. The per-module table in §2 is regenerated bypython scripts/registry_stats.py --table. CI guards against drift viapython scripts/registry_stats.py --check.
1 · Cross-ecosystem lines-of-code¶
| Ecosystem / Project | Method | Files | Lines of code | Primary focus |
|---|---|---|---|---|
StatsPAI src/statspai/ |
measured | 1,276 | 735,758 | validation-tiered causal inference |
StatsPAI tests (tests/) |
measured | 2,340 | 503,223 | — |
| statsmodels 0.14.x | measured | 948 | 381,981 | GLM / time series / general |
| linearmodels | measured | 131 | 36,607 | panel / IV |
| Python causal-inference subtotal | 1,079 | 418,588 | ||
Stata 18 — official .ado |
measured | 3,767 | 937,543 | command layer above closed kernel |
Stata 18 — official .mata |
measured | 411 | 103,822 | Mata numerical layer |
| Stata official executable code | measured | 4,178 | 1,041,365 | (+ 738,543 lines of .sthlp help text, not counted as code) |
| Stata SSC (third-party) | estimated | ~3,500 pkgs | 2M – 4M | community extensions; local sample (reghdfe + winsor2 + 50 others) = 33,296 LOC |
| R base interpreter (C + R + Fortran) | estimated | — | ~1.5M | language itself |
| R base library (73 recommended pkgs) | measured | 509 | 62,321 | shipped with R on this machine |
| CRAN (~22,000 packages, 2026) | estimated | — | 80M–120M (R-only; >200M incl. C/C++/Fortran) | main R package universe |
| Bioconductor (~2,300 packages) | estimated | — | 30M–50M | bioinformatics |
| R ecosystem total | estimated | — | ≈ 150M+ |
How to read this table
LOC is a vanity metric in isolation — what matters is coverage density within a target domain. StatsPAI is deliberately scoped at causal inference and applied econometrics; it is not trying to match R's 150M+ lines because ~90% of CRAN is bioinformatics, visualization, text mining, and general-purpose ML that is out of scope. The relevant comparison is the coverage matrix in §3 below.
2 · StatsPAI per-module breakdown¶
Sorted by LOC. This table is generated from the live source tree by python scripts/registry_stats.py --table; it intentionally avoids editorial focus labels so the numbers can be refreshed mechanically before a release.
| Module | LOC | Files | Registered functions (sp.*) |
|---|---|---|---|
did |
60,280 | 87 | 99 |
agent |
51,145 | 83 | 4 |
regression |
35,376 | 47 | 65 |
timeseries |
34,668 | 63 | 88 |
synth |
32,524 | 47 | 56 |
rd |
23,783 | 35 | 57 |
smart |
17,011 | 21 | 31 |
forest |
16,856 | 18 | 33 |
matching |
18,726 | 29 | 44 |
inference |
17,632 | 34 | 59 |
output |
14,926 | 22 | 42 |
mcmc |
14,643 | 25 | 48 |
diagnostics |
15,478 | 28 | 47 |
core |
15,238 | 27 | 7 |
panel |
15,329 | 25 | 29 |
dml |
10,333 | 25 | 17 |
decomposition |
9,940 | 19 | 32 |
iv |
9,274 | 17 | 10 |
fast |
9,455 | 20 | 0 |
spatial |
8,453 | 32 | 40 |
dag |
7,048 | 12 | 27 |
plots |
8,692 | 21 | 9 |
causal_discovery |
6,220 | 15 | 26 |
doe |
5,879 | 14 | 24 |
multilevel |
5,638 | 9 | 11 |
bayes |
5,408 | 12 | 20 |
metalearners |
5,342 | 10 | 25 |
mendelian |
5,201 | 13 | 41 |
frontier |
4,995 | 8 | 12 |
structural |
6,108 | 15 | 15 |
utils |
6,961 | 15 | 42 |
survival |
6,714 | 12 | 18 |
tmle |
6,459 | 11 | 15 |
workflow |
4,549 | 5 | 3 |
gmm |
4,305 | 12 | 4 |
qte |
4,129 | 9 | 12 |
robustness |
4,099 | 8 | 14 |
network |
3,514 | 9 | 33 |
neural_causal |
3,450 | 6 | 18 |
postestimation |
5,945 | 11 | 20 |
bounds |
3,328 | 7 | 12 |
interference |
3,789 | 12 | 23 |
bartik |
3,209 | 6 | 9 |
causal_llm |
3,025 | 10 | 15 |
experimental |
4,615 | 10 | 23 |
conformal_causal |
2,947 | 10 | 22 |
rlasso |
2,859 | 7 | 10 |
crossval |
2,812 | 7 | 2 |
epi |
2,666 | 6 | 21 |
mediation |
3,640 | 9 | 7 |
datasets |
2,632 | 4 | 5 |
selection |
2,557 | 5 | 8 |
nonparametric |
3,117 | 6 | 14 |
fixest |
4,938 | 7 | 4 |
policy_learning |
2,121 | 5 | 8 |
question |
2,103 | 3 | 6 |
power |
2,061 | 5 | 15 |
proximal |
2,061 | 8 | 13 |
causal_text |
1,774 | 4 | 4 |
survey |
3,426 | 10 | 12 |
bcf |
1,792 | 5 | 8 |
ope |
1,869 | 4 | 9 |
fairness |
1,500 | 3 | 9 |
target_trial |
1,475 | 7 | 9 |
bunching |
1,449 | 5 | 8 |
bridge |
1,345 | 8 | 2 |
principal_strat |
1,238 | 2 | 3 |
mht |
1,194 | 3 | 7 |
causal_rl |
1,103 | 5 | 12 |
dtr |
1,100 | 5 | 8 |
longitudinal |
1,076 | 3 | 7 |
dose_response |
1,089 | 3 | 5 |
imputation |
1,133 | 4 | 4 |
surrogate |
1,061 | 2 | 3 |
gformula |
987 | 3 | 4 |
deepiv |
891 | 2 | 2 |
transport |
834 | 5 | 10 |
multivariate |
849 | 3 | 4 |
msm |
1,091 | 3 | 3 |
causal_impact |
954 | 3 | 3 |
assimilation |
708 | 3 | 4 |
matrix_completion |
685 | 3 | 2 |
multi_treatment |
522 | 2 | 2 |
compat |
379 | 2 | 0 |
censoring |
381 | 2 | 2 |
quasi |
357 | 2 | 2 |
geolift |
183 | 2 | 1 |
checks |
154 | 2 | 0 |
causal |
124 | 1 | 0 |
schemas |
0 | 0 | 0 |
| Total | 634,029 | 1071 | 1562 |
| ## 3 · Causal-inference coverage matrix (full) |
Legend: B = broad API coverage within this comparison table; Y = implemented entry points; P = partial, scattered, or single-algorithm support; N = no first-class entry point. These are API-breadth labels, not validation tiers.
"Stata" = official + major SSC packages. "R" = CRAN. "sm+lm" = statsmodels + linearmodels.
| # | Method family | Stata | R | sm+lm | DoubleML | StatsPAI | StatsPAI entry points |
|---|---|---|---|---|---|---|---|
| 1 | DiD — staggered (CS / SA / BJS / dCdH / Gardner / Wooldridge ET) + event-study + honest CIs (Rambachan-Roth) | P | Y | N | N | B | sp.callaway_santanna, sp.sun_abraham, sp.borusyak_jaravel_spiess, sp.dchd, sp.gardner_did, sp.wooldridge_did, sp.honest_did, sp.cs_report |
| 2 | IV — classical (2SLS / LIML / GMM) + modern (Kernel IV / Deep IV / KAN-DeepIV) | Y classical only | Y classical only | P classical (lm) | P | B | sp.ivreg, sp.kernel_iv, sp.deep_iv, sp.kan_deepiv |
| 3 | RD — CCT sharp/fuzzy/kink + 2D / boundary + multi-cutoff + honest CIs + ML-CATE (18+ estimators) | P (rdrobust SSC) |
Y (rdrobust) |
N | N | B | sp.rdrobust, sp.rd2d, sp.rdhte, sp.rd_forest, sp.rd_boost, sp.rdrandinf, sp.rdpower, sp.rdsummary |
| 4 | Synthetic Control (ADH / ASCM / gsynth / BSTS / Bayesian / PenSCM / FDID — 20 methods + 6 inference strategies) | P (synth SSC) |
P (7 pkgs: Synth, gsynth, CausalImpact, MSCMT, …) | N | N | B | sp.synth(method=...), sp.synth_compare, sp.synth_recommend, sp.synth_power, sp.synth_sensitivity |
| 5 | Matching — PS / CEM / optimal / cardinality / one-to-many | P (psmatch2 SSC) |
Y (MatchIt, optmatch) |
N | N | Y | sp.match, sp.cem, sp.optimal_match, sp.cardinality_match |
| 6 | Double / Debiased ML | N | Y (DoubleML) |
N | Y | Y | sp.dml(model=...), sp.dml_model_averaging, sp.kernel_dml |
| 7 | Meta-Learners (S/T/X/R/DR) + Causal Forest / GRF family | N | Y (grf, rlearner) |
N | N | Y | sp.s_learner, sp.t_learner, sp.x_learner, sp.r_learner, sp.dr_learner, sp.causal_forest, sp.iv_forest, sp.multi_arm_forest, sp.lm_forest, sp.causal_survival_forest, sp.quantile_forest, sp.survival_forest |
| 8 | TMLE / HAL-TMLE | N | Y (tmle, hal9001) |
N | N | Y | sp.tmle, sp.hal_tmle, sp.ctmle |
| 9 | Neural causal (TARNet / CFRNet / DragonNet / CEVAE) | N | N | N | N | B | sp.tarnet, sp.cfrnet, sp.dragonnet, sp.cevae |
| 10 | Causal discovery (NOTEARS / PC / LiNGAM / GES + deep variants) | N | P (pcalg) |
N | N | B | sp.notears, sp.pc_algorithm, sp.lingam, sp.ges |
| 11 | Proximal CI (fortified / bidirectional / MTP / double-negative-control / surrogate) | N | P (pci scattered) |
N | N | B | sp.proximal, sp.fortified_pci, sp.bidirectional_pci, sp.pci_mtp, sp.double_negative_control |
| 12 | QTE / distributional TE / CiC / dist-IV / beyond-avg-LATE / HD panel | P (ivqreg) |
P (qte, Counterfactual) |
N | N | Y | sp.qte, sp.qdid, sp.cic, sp.distributional_te, sp.dist_iv |
| 13 | Mendelian randomization (IVW / Egger / median / mode / MR-PRESSO / MVMR / BMA) | N | Y (MendelianRandomization, TwoSampleMR) |
N | N | Y | sp.mr_ivw, sp.mr_egger, sp.mr_median, sp.mr_presso, sp.mvmr, sp.mr_bma |
| 14 | Conformal causal inference (ITE / CATE / density / dose-response / cluster / fair) | N | N | N | N | B | sp.conformal_ite, sp.conformal_cate, sp.conformal_dose_response |
| 15 | Bayesian causal (BCF / ordinal BCF / factor-exposure BCF) | N | P (bcf) |
N | N | Y | sp.bcf, sp.bcf_ordinal, sp.bcf_factor_exposure |
| 16 | Spatial econometrics (weights → ESDA → ML/GMM → GWR/MGWR → panel) | N | P (5 pkgs: spdep, spatialreg, sphet, splm, GWmodel) | N | N | B | 38 functions including sp.sem, sp.sar, sp.gwr, sp.mgwr, sp.splm |
| 17 | Policy learning / OPE | N | P (policytree) |
N | N | Y | sp.policy_tree, sp.policy_value, sp.doubly_robust_ope |
| 18 | Bunching estimation | P (bunching SSC) |
N | N | N | Y | sp.bunching, sp.kink_bunching |
| 19 | Interference / spillover (partial / network / cluster-RCT / HTE) | N | P (interference) |
N | N | B | 18 functions including sp.spillover, sp.cluster_rct, sp.hte_interference |
| 20 | Matrix completion for panels | N | P (gsynth) |
N | N | Y | sp.matrix_completion |
| 21 | Causal MAS (multi-agent LLM causal discovery — arXiv:2509.00987) | N | N | N | N | B | sp.causal_mas, sp.causal_llm.openai_client, sp.causal_llm.anthropic_client |
| 22 | Publication tables (Word / Excel / LaTeX / HTML / Markdown) | P (outreg2) |
P (modelsummary) |
P | N | B | Supported result objects expose .to_word() / .to_excel() / .to_latex() / .to_html() |
| 23 | Agent-native tool-calling schemas (function_schema()) |
N | N | N | N | B | sp.list_functions(), sp.describe_function(), sp.function_schema(), sp.agent.mcp_server |
4 · How to reproduce these numbers¶
# StatsPAI (Python)
find src/statspai -name '*.py' -exec wc -l {} + | tail -1
find tests -name '*.py' -exec wc -l {} + | tail -1
python3 -c "import statspai as sp; print(len(sp.list_functions()))"
# statsmodels + linearmodels
python3 -c "import statsmodels, os; print(os.path.dirname(statsmodels.__file__))"
python3 -c "import linearmodels, os; print(os.path.dirname(linearmodels.__file__))"
find $(python3 -c "import statsmodels,os;print(os.path.dirname(statsmodels.__file__))") -name '*.py' -exec wc -l {} + | tail -1
find $(python3 -c "import linearmodels,os;print(os.path.dirname(linearmodels.__file__))") -name '*.py' -exec wc -l {} + | tail -1
# Stata (macOS default install path)
find /Applications/Stata/ado/base -name '*.ado' | xargs wc -l | tail -1
find /Applications/Stata/ado/base -name '*.mata' | xargs wc -l | tail -1
# R (installed library only; base-interpreter source requires R-<version>.tar.gz)
find /Library/Frameworks/R.framework/Resources/library \( -name '*.R' -o -name '*.r' \) -exec wc -l {} + | tail -1
Ecosystem-wide estimates (SSC / CRAN / Bioconductor totals) are drawn from:
- SSC — Boston College Statistical Software Components archive package listing.
- CRAN — METACRAN and CRAN by topic task views.
- Bioconductor — Bioconductor 3.20 release statistics.
The SSC sample used for extrapolation is the 52-file local install (/Users/brycewang/Library/Application Support/Stata/ado/plus, 33,296 LOC — includes reghdfe, winsor2, and others).
5 · Why we don't lead with "line-count wins"¶
Three reasons a naked LOC comparison is misleading for StatsPAI positioning:
- Vanity metric: 188K vs R's 150M+ tells a reviewer nothing about capability per line. R's CRAN is ~90% out-of-scope (bioinformatics, visualization, text, general ML) — apples to oranges.
- Moving target: CRAN and statsmodels grow every month. A headline number in the README rots quarterly unless regenerated by CI.
- Wrong axis: StatsPAI's differentiator is causal-inference depth in one API — see §3. Most cells where we win are ❌ in Stata / R / statsmodels entirely, not "fewer lines".
The coverage matrix in §3 is the honest positioning. LOC in §1 is supporting evidence for "yes, there is real code behind these claims" — not a competitive boast.