EDI 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 are in-progress (built fresh on every run)

EDI's C++ model-fitting kernels are being bound to Python in an active, uncommitted, in-progress scaffold under python/ (a separate effort from this benchmark script — see R/package_metadata/python_bindings_package_spec.md). This script does not own or edit that scaffold; at the top of every run it configures and builds whatever is currently there (via CMake, into an isolated /tmp directory) and imports whatever compiles, so the EDI Time column reflects the real state of that work at run time rather than a stale snapshot — different runs of this script can show different EDI coverage as that scaffold evolves, 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 without one, or where the Wald table needs standard errors these 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 (33 of 47 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***
InferenceContinQuantileRegrcontinuousNAstatsmodelsQuantReg15.56NANA
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***

33 of 47 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 (34 of 47 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 three rows 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
InferenceAllSimpleMeanDiffPooledVarcontinuousNAscipystats.ttest_ind(pooled)0.85NANA
InferenceAllSimpleWilcoxcontinuousNAscipystats.mannwhitneyu0.62NANA
InferenceContinKKGLMMcontinuousNANoneNANANA
InferenceContinLincontinuous0.25statsmodelsOLS(interaction)+summary1.154.54x8.45e-22***
InferenceContinOLScontinuous0.17statsmodelsOLS+summary0.593.42x4.84e-25***
InferenceContinQuantileRegrcontinuousNAstatsmodelsQuantReg+summary22.69NANA
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***
InferenceIncidExactFisherincidenceNAscipystats.fisher_exact1.29NANA
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***

34 of 47 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. Only fast_pchisq_upper is declared in the python/ fast_math stub (python/cpp/bindings_fast_math.cpp), and even that build currently fails to import (an unrelated undefined symbol from other in-progress bindings in the same compiled module — python/build_verify/), so no row has a usable EDI binding yet and every row is grey today.

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.