pyGAM: Penalized B-Spline GAMs With a scikit-learn-Style API
[CONTRIBUTORS WELCOME] Generalized Additive Models in Python
At a glance
- What is it?
- pyGAM fits generalized additive models in Python using penalized B-splines, with an interface aimed at scikit-learn and scipy users. It is a good fit when you need per-feature smooth curves you can plot and defend, and a poor fit when you need automatic interactions or a GPU training path.
- Who is it for?
- Adopt pyGAM when the deliverable is a model whose per-feature effect curves can be plotted and explained, and when the additive assumption is defensible for your data. Do not adopt it as a drop-in replacement for gradient boosting on tabular problems where interactions carry most of the signal, because the model form is additive and the library does not search for interaction terms.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 147 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The model form pyGAM commits to
The README states the model directly: g(E[y|X]) = beta_0 + f_1(X_1) + f_2(X_2) + ... + f_p(X_p). There is one term per feature and no cross terms. That single equation is the whole design constraint. Each f_i is built from penalized B-splines, so the shape of the relationship between a feature and the response is estimated rather than typed in by hand. You do not write log(x), x squared, or a knot list. The spline basis supplies the flexibility and a penalty on the coefficients keeps the curve from chasing noise.
This matters for a specific kind of work. If your team currently fits a logistic regression, inspects coefficients, and then argues about whether a variable should have been binned or logged, pyGAM removes that argument by estimating the transformation. The README makes the interpretability claim in additive terms: because the terms are additive, you can examine the effect of each X_i on y while holding the other predictors constant. That is a stronger statement than feature importance from a tree ensemble, which tells you a variable was used but not in which direction or at what thresholds.
The audience is therefore narrower than "Python users who want to predict a number". It is analysts, scientists, and engineers who have to hand a model to someone who will ask why a particular record got its score.
Penalized B-splines as the fitting mechanism
The basis expansion is the part that does the work. pyGAM represents each smooth as a set of B-spline basis functions, so a single feature becomes a small matrix of columns, and the linear predictor is a weighted sum of those columns. Fitting is then a penalized regression problem in that expanded space, which is why the README notes that most of pyGAM's computations are linear algebra operations.
The penalty is what separates this from plain polynomial expansion. A high-degree polynomial fit on a single variable will oscillate at the edges of the data range and produce predictions that swing wildly outside it. A penalized spline basis keeps the curve smooth by construction, and the amount of smoothing is controlled by a parameter rather than by the number of basis functions alone. The README frames this as the reason the approach can "automatically model non-linear relationships" without manually testing many transformations on each variable.
One consequence worth stating plainly: the fitted object is not a set of coefficients you read off a table. It is a set of smooth functions. Any explanation you produce will be a plot of f_i against X_i, not a number with a standard error attached in the way a linear model gives you. If your reporting pipeline expects coefficient tables, the output format changes.
Installation and the MKL acceleration caveat
The README gives a single install line: pip install pygam. The package is also published on conda-forge, per the badge table.
The acceleration note is the part most readers skip and then hit later. The README says that to speed up optimization on large models with constraints, it helps to have Intel MKL installed, and that installing a NumPy linked to MKL routines with Conda is "a bit tricky" because you have to be careful about which channel you use. It also states that pip's NumPy-MKL is outdated. The suggested alternative is a third-party build:
pip install numpy scipy --extra-index-url https://urob.github.io/numpy-mkl
Read that as a warning about environment fragility rather than a performance promise. The README does not quantify the speedup, and it does not say which model sizes cross the threshold where the MKL build starts to matter. If you are fitting small models on a few thousand rows, the default wheels are likely fine and the extra index is unnecessary risk.
For contributors, the README gives the editable install with developer extras: pip install -e ".[dev]" after upgrading pip, and py.test -s to run the suite from the repository root. Contributions are explicitly requested, including new distributions and link functions, which is a signal about where the library's extension surface is.
Where the additive assumption breaks down
The limitation is the model form itself. If the true relationship between two features and the response depends on their combination, a sum of univariate smooths cannot represent it. A model that predicts well only when it knows that a treatment helps for older patients and hurts for younger ones has an interaction, and pyGAM's stated form has no term for that.
There is a second, quieter failure mode: extrapolation. Spline bases are defined over a knot range derived from the training data. Predictions for feature values outside that range are produced by the basis functions at the boundary, and the README does not describe any guard against this. In production, a feature that drifts outside its training range will still return a number.
The third issue is the smoothing parameter. The README does not explain how lam is selected in the material available here beyond pointing at the documentation, so treat model selection as work you own. A single global lam applied across all features assumes every smooth deserves the same amount of penalization, which is rarely true. If you cannot state how you chose lam and how you validated it, you have not finished the analysis, regardless of how the curves look.
Finally, the README says contributions are welcome and lists working on known bugs, improving documentation, and adding distributions and link functions as ways to help. That is a normal open source posture, but it also means coverage of the distribution and link surface is a function of who has contributed so far.
pyGAM against statsmodels GLM and gradient boosting
The nearest neighbor in spirit is a generalized linear model fitted with a formula interface, as in statsmodels. The difference in approach is the basis. A GLM with a formula like y ~ x1 + x2 estimates one coefficient per feature and requires you to specify any transformation in the formula itself; the functional form is your decision. pyGAM replaces that decision with a penalized spline basis and estimates the shape. You give up the compact coefficient table and gain the ability to model curvature without hand-specifying it.
The other comparison is gradient boosting. A boosted tree ensemble will typically find interactions on its own and will often win on raw predictive accuracy for tabular data. What it will not give you is a single smooth curve per feature that you can plot and describe. Feature importance scores and SHAP values are approximations of the model's behavior; a pyGAM smooth is the model's behavior for that term, because the term is additive. That distinction is the reason to accept the accuracy trade-off, and it is the only reason. If nobody is going to look at the curves, the trade is not worth making.
The README cites Hastie, Tibshirani and Friedman, Wood's 2006 book, and Eilers and Marx on P-splines, so the statistical lineage is standard and the references are there if you need to defend the method to a reviewer.
Versioning, licence, and upgrade cost
The release cadence visible in the material is active: v0.10.1 in July 2025, v0.11.0 in November 2025, and v0.12.0 in December 2025, with the last push to the repository in April 2026. The project is not archived. The README carries a version banner pointing at the release notes, so the project treats the changelog as the place to look for behaviour changes.
The version numbering is pre-1.0. In practice that means minor version bumps can carry API changes, and the README does not offer a stability guarantee for the public interface. If you pin pygam in a requirements file, pin it to an exact version and read the release notes before moving. The 0.10 to 0.12 sequence is three releases in roughly five months, which is a fast enough pace that an unpinned dependency will move under you.
The licence is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant. The repository also carries a badge indicating GC.OS sponsorship. Nothing in the supplied material describes a contributor licence agreement or a change of licence, and this is not legal advice: if you are redistributing pyGAM inside a product, have your own counsel read the LICENSE file rather than relying on a badge.
There is also a citation requirement in spirit if not in licence. The README asks that you cite the Zenodo DOI, 10.5281/zenodo.1208723, if the package helped your research. That is a low-cost obligation and worth honoring in published work.
Editorial conclusion
Adopt pyGAM when the deliverable is a model whose per-feature effect curves can be plotted and explained, and when the additive assumption is defensible for your data. Do not adopt it as a drop-in replacement for gradient boosting on tabular problems where interactions carry most of the signal, because the model form is additive and the library does not search for interaction terms. Before committing, verify three things: that your scipy and NumPy versions satisfy the pinned extras in pyproject.toml, whether a linear algebra backend with MKL is available in your environment, and how the lam grid behaves on a held-out split for your own data rather than on the tutorial datasets.
Community notes