EDI v1.0.0.post3 Python Baseline Benchmarks

Generated: 2026-08-17 14:51:03 UTC

Python analog of benchmark_model_fits_R.html — same three tables (point-estimate, Wald/full-inference, utility math kernels), same table shape, same three-color row coding. Produced by R/benchmark/benchmark_model_fits_python.py.

Status: EDI Python bindings (built fresh on every run)

EDI's C++ model-fitting kernels are bound to Python in the edi_kernels package under python/ (released on PyPI — see R/package_metadata/python_bindings_package_spec.md). This script does not own or edit that package; at the top of every run it configures and builds its _core pybind11 module (via CMake, into an isolated /tmp directory) and imports whatever compiles, so the EDI Time column reflects the real state of the bindings at run time rather than a stale snapshot — different runs of this script can show different EDI coverage as the bindings evolve, and a build failure there simply leaves every EDI column NA (not a crash of this script). Point-estimate rows with a working, wired binding call it directly; rows where the Wald table needs standard errors the bindings don't yet expose stay NA. Same-input, same-process discipline: for every wired row, both the EDI and canonical closures are built from the exact same generated arrays (never redrawn separately). Each side is timed at its own fastest available point-estimate-only entry point — the same convention the R report already uses (R's canonical glm.fit() point-estimate row likewise never forms the explicit variance-covariance matrix; that only happens in the separate Wald table). EDI rows therefore use estimate_only=True where that argument exists. Where statsmodels' public API has no equivalent cheaper mode — .fit() is the only entry point it exposes, full stop — the canonical column still reflects .fit()'s real, honest cost: that is genuinely the fastest a Python user can get this model fit today, not a handicap imposed on canonical Python. (Checked directly: statsmodels' .fit() doesn't secretly race ahead by skipping something EDI bothers to compute — accessing .bse after .fit() costs no measurable additional time, since IRLS's per-iteration weighted-least-squares solve already forms the same (X'WX)⁻¹ a covariance estimate would need; the two APIs just draw the "point estimate vs. full inference" line in different places.)

Benchmark Dataset Specification

Methodology

Point-Estimate Results (32 of 46 rows timed, 14 Baseline Gaps)

EDI faster, significant   NA timing comparison   no canonical Python implementation

Families with no clean, actively-maintained Python canonical equivalent — an absent/NA comparison here is more honest than a mismatched substitute baseline (same discipline python_bindings_package_spec.md and the R report apply):

ClassResponseEDI Time (ms)Canonical PkgCanonical FuncCanonical Time (ms)SpeedupTiming Pval
InferenceAllSimpleWilcoxcontinuous0.15numpymedian(HL pairwise diff)0.614.00x5.61e-31***
InferenceContinKKGLMMcontinuousNANoneNANANA
InferenceContinOLScontinuous0.02numpylinalg.lstsq0.084.44x4.02e-27***
InferenceContinRobustRegrcontinuous0.10statsmodelsRLM8.1377.85x2.21e-28***
InferenceCountHurdleNegBincount1.59statsmodelsHurdleCountModel(negbin)89.6356.33x4.72e-29***
InferenceCountHurdlePoissoncount0.98statsmodelsHurdleCountModel(poisson)27.7128.21x3.18e-27***
InferenceCountKKCondPoissonOneLikcountNANoneNANANA
InferenceCountKKGLMMcountNANoneNANANA
InferenceCountKKHurdlePoissonOneLikcountNANoneNANANA
InferenceCountNegBincount0.64statsmodelsNegativeBinomial12.0518.90x1.18e-29***
InferenceCountPoissoncount0.11statsmodelsGLM(Poisson)2.6724.70x4.45e-30***
InferenceCountQuasiPoissoncount0.10statsmodelsGLM(Poisson)2.6427.16x1.55e-31***
InferenceCountRobustPoissoncount0.08statsmodelsGLM(Poisson)2.4029.38x4.13e-29***
InferenceCountZeroInflatedNegBincount9.45statsmodelsZeroInflatedNegativeBinomialP218.3923.11x1.74e-30***
InferenceCountZeroInflatedPoissoncount7.93statsmodelsZeroInflatedPoisson39.635.00x5.63e-25***
InferenceIncidBinomialIdentityRiskDiffincidence0.12statsmodelsGLM(Binomial, identity link)5.8348.07x9.04e-30***
InferenceIncidGCompRiskDiffincidence0.24statsmodelsGLM(Binomial)+gcomp(RD)2.6311.16x7.72e-27***
InferenceIncidGCompRiskRatioincidence0.22statsmodelsGLM(Binomial)+gcomp(RR)2.7112.15x2.85e-28***
InferenceIncidKKCondLogitGLMMOneLikincidenceNANoneNANANA
InferenceIncidLogBinomialincidence0.89statsmodelsGLM(Binomial, log link)10.4811.76x2.5e-16***
InferenceIncidLogRegrincidence0.18statsmodelsGLM(Binomial)2.5213.71x5e-33***
InferenceIncidModifiedPoissonincidence0.11statsmodelsGLM(Poisson)2.4522.42x9.31e-30***
InferenceIncidProbitRegrincidence0.36statsmodelsGLM(Binomial, probit link)5.2314.35x1.07e-25***
InferenceIncidRiskDiffincidence0.02numpylinalg.lstsq (LPM)0.074.42x8.75e-21***
InferenceOrdinalAdjCatLogitRegrordinal0.55NoneNANANA
InferenceOrdinalCauchitRegrordinal0.68NoneNANANA
InferenceOrdinalCloglogRegrordinal0.58NoneNANANA
InferenceOrdinalContRatioRegrordinal0.19NoneNANANA
InferenceOrdinalGCompMeanDiffordinal0.76statsmodelsOrderedModel(logit)+gcomp69.7191.47x1.29e-31***
InferenceOrdinalKKCLMMordinalNANoneNANANA
InferenceOrdinalKKGLMMordinalNANoneNANANA
InferenceOrdinalOrderedProbitRegrordinal0.57statsmodelsOrderedModel(probit)62.28109.22x1.15e-24***
InferenceOrdinalPropOddsRegrordinal0.70statsmodelsOrderedModel(logit)77.94111.19x2.49e-28***
InferenceOrdinalStereotypeLogitRegrordinalNANoneNANANA
InferencePropBetaRegrproportion1.06statsmodelsBetaModel11.3910.72x5.64e-31***
InferencePropFractionalLogitproportion0.11statsmodelsGLM(Binomial, fractional y)2.0918.21x2.41e-27***
InferencePropGCompMeanDiffproportion0.17statsmodelsGLM(Binomial)+gcomp1.9311.06x4.07e-32***
InferencePropZeroOneInflatedBetaRegrproportion4.66NoneNANANA
InferenceSurvivalCoxPHRegrsurvival0.38scikit-survivalCoxPHSurvivalAnalysis34.7590.41x1.38e-22***
InferenceSurvivalKKWeibullFrailtyOneLiksurvivalNANoneNANANA
InferenceSurvivalKMDiffsurvival0.01lifelinesKaplanMeierFitter(median)14.821365.87x2.57e-29***
InferenceSurvivalLogRanksurvival0.08lifelinesstatistics.logrank_test16.24209.33x3.63e-27***
InferenceSurvivalRestrictedMeanDiffsurvival0.01lifelinesutils.restricted_mean_survival_time17.561603.59x2.49e-35***
InferenceSurvivalStratCoxPHRegrsurvival0.35lifelinesCoxPHFitter(strata=)152.53432.83x5.82e-27***
InferenceSurvivalWeibullRegrsurvival0.12lifelinesWeibullAFTFitter.fit() (right-censored only)136.181169.57x2.29e-31***
InferenceSurvivalWeibullRegrsurvival0.37lifelinesWeibullAFTFitter.fit_interval_censoring()192.87518.83x4.32e-31***

32 of 46 rows have a working canonical Python timing; 0 canonical fit(s) failed on this run (see console log); 14 are documented Baseline Gaps.

Wald Test Performance / Full Inference (30 of 43 rows timed, 13 Baseline Gaps)

EDI faster, significant   NA timing comparison   no canonical Python implementation

Same Baseline Gap families as the point-estimate table above, plus 1 row(s) excluded outright (no variance computation, not a Baseline Gap in the usual sense):

Baseline Gap families (same as point-estimate table)
ClassResponseEDI Time (ms)Canonical PkgCanonical FuncCanonical Time (ms)SpeedupTiming Pval
InferenceContinKKGLMMcontinuousNANoneNANANA
InferenceContinLincontinuous0.25statsmodelsOLS(interaction)+summary1.154.54x8.45e-22***
InferenceContinOLScontinuous0.17statsmodelsOLS+summary0.593.42x4.84e-25***
InferenceContinRobustRegrcontinuous0.29statsmodelsRLM+summary5.8320.20x3.24e-29***
InferenceCountHurdleNegBincount1.37statsmodelsHurdleCountModel(negbin)+summary86.0262.90x6.37e-30***
InferenceCountHurdlePoissoncount1.28statsmodelsHurdleCountModel(poisson)+summary26.9121.02x1.08e-29***
InferenceCountKKCondPoissonOneLikcountNANoneNANANA
InferenceCountKKGLMMcountNANoneNANANA
InferenceCountKKHurdlePoissonOneLikcountNANoneNANANA
InferenceCountNegBincount0.78statsmodelsNegativeBinomial+summary13.2817.09x2.39e-26***
InferenceCountPoissoncount0.20statsmodelsGLM(Poisson)+summary2.6813.40x6.18e-29***
InferenceCountQuasiPoissoncount0.21statsmodelsGLM(Poisson)+summary2.7813.27x3.5e-31***
InferenceCountRobustPoissoncount0.22statsmodelsGLM(Poisson)+summary2.8512.85x1.07e-31***
InferenceCountZeroInflatedNegBincount4.43statsmodelsZeroInflatedNegativeBinomialP+summary216.0548.73x1.05e-27***
InferenceCountZeroInflatedPoissoncount12.07statsmodelsZeroInflatedPoisson+summary35.842.97x8.69e-28***
InferenceIncidGCompRiskDiffincidence1.19statsmodelsGLM(Binomial)+gcomp(RD)+delta-method SE3.492.93x8.34e-30***
InferenceIncidGCompRiskRatioincidence1.17statsmodelsGLM(Binomial)+gcomp(RR)+delta-method SE3.272.79x4.2e-24***
InferenceIncidKKCondLogitGLMMOneLikincidenceNANoneNANANA
InferenceIncidLogBinomialincidence0.52statsmodelsGLM(Binomial, log link)+summary4.488.56x9.24e-31***
InferenceIncidLogRegrincidence0.26statsmodelsGLM(Binomial)+summary2.469.38x1.82e-26***
InferenceIncidMiettinenNurminenRiskDiffincidence0.03statsmodelsstats.proportion.confint_proportions_2indep(score)2.6592.63x1.55e-27***
InferenceIncidNewcombeRiskDiffincidence0.000303statsmodelsstats.proportion.confint_proportions_2indep(newcomb)0.19624.56x2.49e-24***
InferenceIncidProbitRegrincidence0.47statsmodelsGLM(Binomial, probit link)+summary4.459.45x7.71e-27***
InferenceIncidRiskDiffincidence0.18statsmodelsOLS+summary (LPM)0.613.40x5.2e-24***
InferenceOrdinalAdjCatLogitRegrordinalNANoneNANANA
InferenceOrdinalContRatioRegrordinalNANoneNANANA
InferenceOrdinalGCompMeanDiffordinal2.92statsmodelsOrderedModel(logit)+gcomp+delta-method SE96.6133.13x2.82e-29***
InferenceOrdinalJonckheereTerpstraTestordinalNANoneNANANA
InferenceOrdinalKKCLMMordinalNANoneNANANA
InferenceOrdinalKKGLMMordinalNANoneNANANA
InferenceOrdinalPropOddsRegrordinal0.85statsmodelsOrderedModel(logit)+summary76.8390.64x1.99e-29***
InferenceOrdinalRiditordinal0.09numpymanual ridit computation0.283.17x4.82e-22***
InferenceOrdinalStereotypeLogitRegrordinalNANoneNANANA
InferencePropBetaRegrproportion1.08statsmodelsBetaModel+summary8.367.77x5.39e-25***
InferencePropGCompMeanDiffproportion1.03statsmodelsGLM(Binomial)+gcomp+delta-method SE2.872.79x1.06e-26***
InferencePropZeroOneInflatedBetaRegrproportionNANoneNANANA
InferenceSurvivalCoxPHRegrsurvival0.48lifelinesCoxPHFitter+summary64.13134.51x1.99e-30***
InferenceSurvivalGehanWilcoxsurvival0.08lifelinesstatistics.logrank_test(weightings='wilcoxon')17.64224.94x4.58e-31***
InferenceSurvivalKKWeibullFrailtyOneLiksurvivalNANoneNANANA
InferenceSurvivalKMDiffsurvival0.01lifelinesKaplanMeierFitter(median)+CI13.931267.95x1.74e-29***
InferenceSurvivalLogRanksurvival0.08lifelinesstatistics.logrank_test15.99197.11x7.95e-29***
InferenceSurvivalStratCoxPHRegrsurvival0.45lifelinesCoxPHFitter(strata=)160.27359.32x1.12e-32***
InferenceSurvivalWeibullRegrsurvival0.19lifelinesWeibullAFTFitter.fit()+summary128.70676.59x4.37e-23***

30 of 43 rows have a working canonical Python timing; 0 canonical fit(s) failed on this run (see console log); 13 are documented Baseline Gaps; 1 rows from the point-estimate table are excluded entirely (see above).

Utility / Math Kernel Performance (14 of 14 functions timed)

EDI faster, significant   NA timing comparison   no canonical Python implementation

EDI's internal fast_* scalar math kernels — every one that exists in R/EDI/src (fast_digamma, fast_trigamma, fast_lgamma, fast_lbeta, fast_qnorm, fast_log_pnorm, fast_log_dnorm, fast_dnbinom_mu, fast_pchisq_upper, fast_erfc, pnorm_fast, dnorm_fast, fast_atan, fast_log1pexp) — vs. their scipy/numpy vectorized equivalents, over a length-5000 vector. All 14 are bound in python/cpp/bindings_fast_math.cpp as vectorized elementwise wrappers around the same portable inline scalar functions EDI's model-fitting kernels call internally, so every row times a real EDI call against its canonical vectorized counterpart.

ClassResponseEDI Time (ms)Canonical PkgCanonical FuncCanonical Time (ms)SpeedupTiming Pval
dnorm_fastutility0.03scipystats.norm.pdf0.154.60x1.32e-32***
fast_atanutility0.02numpyarctan0.062.44x8.5e-32***
fast_digammautility0.06scipyspecial.digamma0.091.52x1.24e-20***
fast_dnbinom_muutility0.40scipystats.nbinom.logpmf0.471.15x1.67e-06***
fast_erfcutility0.08scipyspecial.erfc0.091.21x9.94e-09***
fast_lbetautility0.22scipyspecial.betaln0.512.29x3.23e-39***
fast_lgammautility0.07scipyspecial.gammaln0.091.27x7.8e-12***
fast_log1pexputility0.06numpylogaddexp(0,.)0.101.63x8.69e-29***
fast_log_dnormutility0.00179scipystats.norm.logpdf0.1375.01x1.01e-31***
fast_log_pnormutility0.12scipystats.norm.logcdf0.272.30x3.28e-29***
fast_pchisq_upperutility0.55scipystats.chi2.sf0.771.42x7.83e-20***
fast_qnormutility0.03scipystats.norm.ppf0.228.60x5.72e-30***
fast_trigammautility0.02scipyspecial.polygamma(1,.)1.3369.17x3.55e-30***
pnorm_fastutility0.07scipystats.norm.cdf0.202.85x3.81e-27***

14 of 14 functions have a working canonical timing; 0 failed on this run.

See R/package_metadata/python_bindings_package_spec.md for the full kernel-binding plan.