broom-sm 
Tidy-style statistical inference for Python with statsmodels
broom-sm brings the ergonomic design of broom and the tidyverse to Python's statsmodels ecosystem. The package centers around three main verbs—stats_tidy(), stats_glance(), and stats_augment()—supplemented by bootstrapping utilities, diagnostic plots, and Bayesian helpers.
import pandas as pd
import statsmodels.api as sm
from broom_sm import stats_report
# Load data
mtcars = sm.datasets.get_rdataset("mtcars").data
# Fit once, get everything
report = mtcars.stats_report(
formula="mpg ~ wt + hp",
stat_type="ols"
)
# Tidy coefficient table
print(report["tidy"])
#> term estimate std.error conf.low conf.high statistic p.value
#> 0 Intercept 37.227270 1.877627 33.343267 41.111273 19.826764 1.27e-17
#> 1 wt -3.877831 0.714968 -5.355789 -2.399873 -5.423781 1.19e-05
#> 2 hp -0.031157 0.011436 -0.054812 -0.007501 -2.724389 1.12e-02
# Model-level statistics
print(report["glance"])
#> stat_type nobs llf aic bic df_model df_resid rsquared
#> 0 ols 32 -72.54928 153.09856 158.95938 2.0 29.0 0.826783
Installation
# Core package (tidy verbs + diagnostics)
pip install broom-sm
# With visualization dependencies
pip install broom-sm[viz]
# With Bayesian bootstrap support
pip install broom-sm[bayes]
The Tidy Workflow
broom-sm is built around three core verbs that convert statsmodels objects into tidy DataFrames:
| Verb | Purpose | Output |
|---|---|---|
stats_tidy() |
Coefficient tables | One row per term |
stats_glance() |
Model-level statistics | One row per model |
stats_augment() |
Add predictions & residuals | One row per observation |
Example: Analysis of Variance
Test whether vehicle weight differs by cylinder count:
import pandas as pd
import statsmodels.api as sm
mtcars = sm.datasets.get_rdataset("mtcars").data
# Calculate observed statistic
obs_stat = mtcars.stats_anova_tidy(
formula="wt ~ factor(cyl)",
anova_type=2
)
# Bootstrap the null distribution
null_dist = mtcars.boot_tidy(
formula="wt ~ factor(cyl)",
stat_type="ols",
n_boot=1000,
seed=42
)
# Visualize
from broom_sm import stats_residual_plot
figures = mtcars.stats_residual_plot(["cyl"], y="wt")
figures[0][1].show()
# Calculate p-value
from scipy import stats
f_stat = obs_stat["statistic"].iloc[0]
p_value = 1 - stats.f.cdf(f_stat, obs_stat["df"].iloc[0], obs_stat["df_resid"].iloc[0])
Key Features
🔁 Tidy Verbs
All core verbs work with formulas or pre-fitted statsmodels results:
# Formula interface
df.stats_tidy("y ~ x1 + x2", stat_type="ols")
# Pre-fitted model interface
import statsmodels.formula.api as smf
model = smf.ols("y ~ x1 + x2", data=df).fit()
df.stats_tidy(model=model)
🧱 Extensible Model Registry
Support for OLS, GLMs (Poisson, Gamma, Beta, Negative Binomial), GEE, MixedLM, PHReg/Survival, and Quantile Regression. Register custom models:
from broom_sm.model_registry import ModelSpec, register_model
import statsmodels.formula.api as smf
register_model(
"tobit",
ModelSpec(
fitter=lambda formula, data, **kwargs: smf.tobit(formula, data=data, **kwargs).fit(),
stat_name="z_stat"
)
)
📦 Bootstrapping
Built-in resampling with consistent logging:
boot = mtcars.boot_tidy(
formula="mpg ~ wt",
stat_type="ols",
n_boot=500,
seed=11
)
boot.groupby("term")["estimate"].agg(["mean", "std"])
📊 Diagnostics & Visualization
All plot helpers return Matplotlib figures (no implicit plt.show()):
# Residual diagnostics
figures = df.stats_residual_plot(["x1", "x2"], y="y")
# Influence plot
fig = df.stats_influence_plot("y ~ x1 + x2", stat_type="ols")
# Coefficient forest plot
tidy = df.stats_tidy("y ~ x1 + x2", stat_type="ols")
fig, ax = stats_coef_forest(tidy)
🧪 Robust Standard Errors
Pass cov_type, cov_kwds, family, link, or weights directly:
df.stats_tidy(
formula="mpg ~ wt",
stat_type="glm",
family="binomial",
weights=df["weights"],
cov_type="HC3"
)
🛠️ Command-Line Interface
Quick reports from the terminal:
# Single model report
broom-sm report --data data.csv --formula 'y ~ x1 + x2' --stat-type ols
# Compare multiple models
broom-sm compare --data data.csv --stat-type ols \
--formulas "y ~ x1" "y ~ x1 + x2"
Output defaults to JSON; pass --format csv for tabular output.
🔗 widyr Integration (R parity)
broom-sm now includes a Python port of core
widyr verbs for tidy pairwise and
wide-matrix workflows:
pairwise_count,pairwise_cor,pairwise_dist,pairwise_similaritypairwise_pmi,pairwise_deltawidely_svd,widely_kmeans,widely_hclust,widely,squarelycor_sparse
from broom_sm import pairwise_cor, widely_kmeans
# Pairwise country similarity by life expectancy trajectories
corr = pairwise_cor(gapminder, "country", "year", "lifeExp", method="pearson")
# Cluster countries in wide feature space
clusters = widely_kmeans(gapminder, "country", "year", "lifeExp", k=3, random_state=0)
All pairwise outputs use tidy columns (item1, item2, metric column), and
the module is fully exported from broom_sm.__init__.
Model Coverage
| Model Type | stat_type |
Robust SEs | Weights | Family/Link |
|---|---|---|---|---|
| OLS | "ols" |
✅ | ✅ | — |
| GLM (Gaussian) | "glm" |
✅ | ✅ | ✅ |
| GLM (Poisson) | "poisson" |
✅ | ✅ | ✅ |
| GLM (Gamma) | "gamma" |
✅ | ✅ | ✅ |
| GLM (Beta) | "beta" |
✅ | ✅ | ✅ |
| Negative Binomial | "negbin" |
✅ | ✅ | — |
| Quantile Regression | "quantreg" |
✅ | ✅ | — |
| GEE | "gee" |
✅ | ✅ | ✅ |
| MixedLM | "mixedlm" |
✅ | ✅ | — |
| PHReg (Survival) | "phreg" |
✅ | ✅ | — |
| Logit / Binomial | "logit" |
✅ | ✅ | ✅ |
Tidy Diagnostics (broom + broomExtra parity)
The package also exposes tidy wrappers for diagnostic, model-comparison, and
inference helpers — many of which are parity work for R's broom and
broomExtra:
| broom-sm function | Equivalent R helper | Purpose |
|---|---|---|
stats_kendall_tidy |
broom::tidy.Kendall |
Kendall's τ correlation matrix |
stats_coeftest |
lmtest::coeftest |
Wald z-tests for any fitted model |
stats_manova_tidy |
broom::tidy.manova |
One-way MANOVA (Wilks / Pillai / Hotelling-Lawley / Roy) |
stats_rmse |
broomExtra::perf_rmse |
RMSE / MAE / R² per group |
stats_roc_tidy |
broomExtra::perf_roc |
ROC curve + trapezoidal AUC |
stats_breusch_pagan / stats_white_test |
lmtest::bptest |
Heteroskedasticity tests |
stats_dffits / stats_cooks_distance / stats_leverage |
broom::augment.lm columns |
Influence diagnostics |
stats_crossv_kfold / stats_crossv_mc |
broomExtra::crossv_* |
Tidy cross-validation splits |
See docs/audit_vs_r_broom.md for the full
parity matrix (which verbs are covered, partial, or out-of-scope due to a
missing statsmodels analogue).
Changelog
Version 0.2.0 — 2026-09-02
Added
- New
widyrparity module with tidy pairwise/wide verbs:pairwise_count,pairwise_cor,pairwise_dist,pairwise_similarity,pairwise_pmi,pairwise_delta,widely_svd,widely_kmeans,widely_hclust,widely,squarely, andcor_sparse. - Public exports for the full
widyrsurface frombroom_sm.__init__. - New integration coverage in
tests/test_widyr.pyfor pairwise outputs, upper-triangle filtering, metric validation, sparse correlation, and clustering/SVD behavior. - New
phreg(Cox PH) support instats_tidy/stats_glance, including synthesizednobs/aic/bicwhen they are missing from fitted results. - New parity helpers:
stats_kendall_tidy,stats_coeftest,stats_manova_tidy,stats_rmse,stats_roc_tidy,stats_breusch_pagan,stats_white_test,stats_dffits,stats_cooks_distance,stats_leverage,stats_crossv_kfold, andstats_crossv_mc. - New AI workflow guide:
docs/howto/ai-assistant.md.
Changed
- GitHub Actions CI is now multi-job with:
- matrix tests on Python 3.10/3.11/3.12
- explicit extras install (
testing,viz,bayes) - Sphinx docs build with warnings treated as errors
- advisory
ruffandmypychecks
- Fixed CI dependency installation by removing invalid
.[dev]. stats_tidyandstats_glancenow use shared coercion/synthesis helpers so numpy-backed statsmodels results are converted to robust tidy/glance output.
Documentation
- Added
docs/audit_vs_r_broom.md, a detailed parity audit againstbroom,broomExtra, andbroom.mixed. - Updated
README.md,docs/index.md,docs/howto/index.md, andCONTRIBUTING.mdfor the AI assistant playbook and CI expectations. - Added parity test summary to
tests/test_parity.py(34 new tests; 96 passed, coverage 93%).
Version 0.1.3 — 2026-07-13
Quality fixes, visual/plotting diagnostics testing, and coverage expansion to 96%:
- Fixed OLS Weights: Changed the direct Ordinary Least Squares fitter registration to use WLS when weights are supplied, making weights functional rather than silent placebos.
- Fixed
stats_augmentNaN alignment: Rewrote alignment logic to assign pandas Series directly (relying on index alignment rather than.values), avoiding length mismatches when rows are dropped. Used pre-transformed exog values for predictions. - Optimized
stats_tidymerges: Replaced consecutive DataFrame merges on the"term"column with direct coefficient construction. - Dependency cleanup: Moved
seaborn,matplotlib, andbayesian_bootstrapto optional package extras, adding guarded imports and descriptive import errors. - Coverage expansion: Created extensive tests for visual diagnostics, CLI parameters (
--index-col), fallback paths, and mocked import environments, raising line coverage to 96% with all 62 tests passing.
Version 0.1.2 — 2026-06-20
P0 fixes from the 2026-06-20 code review:
stats_augmentnow validates index uniqueness fordataandnew_data, rejects overlapping indices, and aligns residuals / influence diagnostics position-wise for the in-sample path. The.in_sampleflag is now a single boolean rather than aset-based membership test.prepare_fitnow passesfreq_weightsto GLM-family fitters (Poisson, Gamma, Negative Binomial, Beta, etc.) and keepsweightsfor OLS.boot_tidy,boot_glance, andboot_augmentraiseRuntimeErrorwhen every bootstrap replication fails, instead of returning an empty DataFrame.stats_residual_plotvalidates that the target columnyis numeric before passing it to plotting /probplot.stats_vifnow emits a clear warning that the intercept is omitted, and handles no-intercept formulas consistently without adding a constant.
Selected P1 fixes in the same release:
anova_typeis validated to be 1, 2, or 3 instats_anova_tidy.stats_kruskal_tidyvalidates thatgroup_colandvalue_colexist.stats_correlation_tidyvalidates that requestedcolumnsexist and are numeric.stats_formulanow quotes non-syntactic column names withQ('...').bayes_bootvalidatestarget_column/n_samplesand warns when NaN values are dropped.stats_chisquare_plotdrops NaN categories before building the contingency table.- Repository URLs in
setup.cfgupdated fromjcvall/broom-smtoezraair555/broom-sm. - Removed the unused
src/extra_smpackage.
Documentation
Full documentation (API, how-to guides, tutorials, and plot gallery) lives in docs/:
- Tutorials — End-to-end walkthroughs
- How-to Guides — Task-oriented recipes
- AI Assistant Workflow — Deterministic patterns for coding agents
- API Reference — Complete function documentation
- Quick Start — Get started in 5 minutes
The rendered site is at https://ezraair555.github.io/broom-sm/.
Contributing
We welcome contributions! Please review our contributing guidelines and Python Software Foundation code of conduct.
For questions and discussions, please post on GitHub Discussions. If you think you've encountered a bug, please submit an issue.
License
MIT License — see LICENSE.txt for details.
Acknowledgments
broom-sm draws inspiration from:
- broom (R) — Tidy model outputs
- infer (R) — Tidy statistical inference
- pandas_flavor — DataFrame method registration
- statsmodels — Statistical modeling in Python
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file broom_sm-0.2.0.tar.gz.
File metadata
- Download URL: broom_sm-0.2.0.tar.gz
- Upload date:
- Size: 877.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5980ec0ff5cb2b6e07632b6b7db549b847131fcbc8a755224900ab466bdc2e4b
|
|
| MD5 |
74b0cff28b414f78a8aa826930b4076a
|
|
| BLAKE2b-256 |
44ccc142d7ca6047ae5d3ea078b74a3226c16157b6da7e2f73efaa7ba487f7d0
|
Provenance
The following attestation bundles were made for broom_sm-0.2.0.tar.gz:
Publisher:
ci.yml on ezraair555/broom-sm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
broom_sm-0.2.0.tar.gz -
Subject digest:
5980ec0ff5cb2b6e07632b6b7db549b847131fcbc8a755224900ab466bdc2e4b - Sigstore transparency entry: 2688498231
- Sigstore integration time:
-
Permalink:
ezraair555/broom-sm@c9a8e79e2e6920ff3aa55459b9ad75f1fe7926d7 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ezraair555
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@c9a8e79e2e6920ff3aa55459b9ad75f1fe7926d7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file broom_sm-0.2.0-py3-none-any.whl.
File metadata
- Download URL: broom_sm-0.2.0-py3-none-any.whl
- Upload date:
- Size: 39.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3be51c4aec9da615faf97e53291a1edf2b127e14b4ba9984e0282f2c7477de9c
|
|
| MD5 |
52ef6e9dd2f3a07d8da42a25f02525b5
|
|
| BLAKE2b-256 |
ed141dd289e3505e879f3abab0c6c5fa49bd1b8b6d9c0fadfb2ef9a7659f3e63
|
Provenance
The following attestation bundles were made for broom_sm-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on ezraair555/broom-sm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
broom_sm-0.2.0-py3-none-any.whl -
Subject digest:
3be51c4aec9da615faf97e53291a1edf2b127e14b4ba9984e0282f2c7477de9c - Sigstore transparency entry: 2688498327
- Sigstore integration time:
-
Permalink:
ezraair555/broom-sm@c9a8e79e2e6920ff3aa55459b9ad75f1fe7926d7 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ezraair555
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@c9a8e79e2e6920ff3aa55459b9ad75f1fe7926d7 -
Trigger Event:
push
-
Statement type: