If you’ve used Stata’s margins command and then tried to replicate the same analysis in Python, you know the gap. StatsModels has get_margeff(), but it covers a fraction of what margins does. I just released smmargins to close that gap.
The Problem Link to heading
StatsModels’ get_margeff() gives you marginal effects at the mean or overall—but that’s where it stops. Stata’s margins does far more: adjusted predictions at arbitrary covariate profiles, subgroup average marginal effects, elasticities, difference-in-differences, joint tests, simultaneous confidence intervals, and flexible specification of counterfactual scenarios.
If you’re migrating from Stata to Python—or just want that level of rigor without leaving your notebook—you were out of luck. Until now.
Why Marginal Effects Matter Link to heading
Model coefficients live in abstract space. A logit coefficient tells you the change in log-odds per unit increase in a predictor. That’s useful for fitting, but it’s not what stakeholders care about.
Marginal effects translate those coefficients into natural terms:
- “A 1-unit increase in income is associated with a 3.2 percentage point decrease in default probability”
- “Treatment A increased outcomes by 1.8 units compared to treatment B, holding other factors constant”
- “The effect of education on earnings varies by gender: 4.2% for men, 5.1% for women”
These are the numbers you put in slides, policy briefs, and executive summaries. smmargins makes it easy to get them right.
What smmargins Does Link to heading
smmargins brings Stata-style margins functionality to StatsModels’ linear models:
- Adjusted predictions at specified covariate values
- Marginal effects (dydx, dyex, eydx, eyex)
- Elasticities with proper chain rule handling
- Difference-in-differences estimates on the correct scale
- Joint Wald tests and pairwise contrasts with simultaneous confidence intervals
It works with any fitted model that exposes params, cov_params(), and predict(params, exog).
Bridging the Gap Link to heading
Here’s what get_margeff() doesn’t give you that smmargins does:
| Feature | get_margeff() | smmargins |
|---|---|---|
| Marginal effects at mean / overall | ✅ | ✅ |
Subgroup AMEs (over=) | ❌ | ✅ |
| Elasticities (eyex, dyex, eydx) | Partial | ✅ |
| Adjusted predictions at covariate grids | ❌ | ✅ |
Counterfactual specification (values=) | ❌ | ✅ |
| Difference-in-differences | ❌ | ✅ |
| Joint Wald tests | ❌ | ✅ |
| Pairwise contrasts | ❌ | ✅ |
| Simultaneous CIs (Bonferroni, Šidák, sup-t) | ❌ | ✅ |
| Robust covariance passthrough (HC0–HC3, cluster, HAC) | Limited | ✅ |
Inference Options Link to heading
Choose your variance-covariance estimator:
- Delta method (fast, analytic derivatives)
- Krinsky-Robb simulation
- Bootstrap
Plus robust covariance passthrough: HC0–HC3, cluster-robust, HAC.
Simultaneous Confidence Intervals Link to heading
Multiple comparisons? Bonferroni gets conservative fast. smmargins supports:
- Bonferroni
- Šidák correction
- sup-t (narrower when contrasts are correlated)
Counterfactual Specification Link to heading
Hold variables at specific values without restating every column:
Margins(model, at={'age': 'mean', 'income': [30000, 50000, 70000]},
over='treatment_group')
Or pass arbitrary frames for out-of-sample evaluation via newdata=.
Verified Against Established Packages Link to heading
The test suite checks parity against:
- StatsModels’ own
get_margeff()(at'overall'and'mean') - R’s
marginaleffectspackage
If the output diverges from either, it’s a bug.
Installation Link to heading
pip install smmargins
Docs are at smmargins.readthedocs.io, and the code is on GitHub.
Who Should Use This Link to heading
- Economists and social scientists migrating from Stata to Python
- Analysts who need subgroup AMEs, DiD, or joint tests that
get_margeff()doesn’t provide - Researchers publishing with marginal effects who want reproducible Python workflows
- ML practitioners who need to communicate model effects in stakeholder-friendly terms