$npx -y skills add pymc-labs/python-analytics-skills --skill pymc-extrasLoad when the user is working with pymc-extras (pmx) features: splines / BSplineBasis, distributional regression / GAMLSS, R2D2M2CP or horseshoe priors, discrete variable marginalization, or Laplace approximation via fit_laplace. Triggers include: pymc_extras, pymc-extras, pmx, s
| 1 | # PyMC-Extras (pmx) — Advanced Extensions |
| 2 | |
| 3 | ```python |
| 4 | import pymc_extras as pmx |
| 5 | ``` |
| 6 | |
| 7 | ## Splines |
| 8 | |
| 9 | ### BSplineBasis |
| 10 | |
| 11 | Create B-spline basis matrices for nonlinear effects: |
| 12 | |
| 13 | ```python |
| 14 | import numpy as np |
| 15 | import pymc as pm |
| 16 | import pymc_extras as pmx |
| 17 | |
| 18 | x = np.linspace(0, 1, 100) |
| 19 | |
| 20 | # Create B-spline basis with 10 knots, degree 3 (cubic) |
| 21 | B = pmx.BSplineBasis(n_knots=10, degree=3) |
| 22 | basis_matrix = B.build(x) # shape: (100, n_basis) |
| 23 | |
| 24 | with pm.Model() as spline_model: |
| 25 | # Coefficients for each basis function |
| 26 | w = pm.Normal("w", mu=0, sigma=1, shape=basis_matrix.shape[1]) |
| 27 | mu = pm.math.dot(basis_matrix, w) |
| 28 | sigma = pm.HalfNormal("sigma", sigma=1) |
| 29 | y = pm.Normal("y", mu=mu, sigma=sigma, observed=y_obs) |
| 30 | ``` |
| 31 | |
| 32 | ### Penalized Splines with Smoothing Priors |
| 33 | |
| 34 | Prevent overfitting by penalizing roughness via random walk priors on coefficients: |
| 35 | |
| 36 | ```python |
| 37 | with pm.Model() as penalized_spline: |
| 38 | # Smoothing parameter controls wiggliness |
| 39 | tau = pm.HalfCauchy("tau", beta=1) |
| 40 | |
| 41 | # Random walk prior on spline coefficients (penalizes second differences) |
| 42 | w = pm.GaussianRandomWalk("w", sigma=tau, shape=basis_matrix.shape[1]) |
| 43 | |
| 44 | mu = pm.math.dot(basis_matrix, w) |
| 45 | sigma = pm.HalfNormal("sigma", sigma=1) |
| 46 | y = pm.Normal("y", mu=mu, sigma=sigma, observed=y_obs) |
| 47 | ``` |
| 48 | |
| 49 | ### ZeroSumNormalBasis |
| 50 | |
| 51 | For identifiable models where basis coefficients must sum to zero: |
| 52 | |
| 53 | ```python |
| 54 | # Ensures sum-to-zero constraint on spline coefficients |
| 55 | B_zs = pmx.ZeroSumNormalBasis(n_knots=10, degree=3) |
| 56 | ``` |
| 57 | |
| 58 | See `references/splines.md` for detailed spline reference. |
| 59 | |
| 60 | ## Distributional Regression (GAMLSS-style) |
| 61 | |
| 62 | Model ALL distribution parameters as functions of covariates, not just the mean. |
| 63 | |
| 64 | ### Basic Pattern |
| 65 | |
| 66 | ```python |
| 67 | with pm.Model() as dist_reg: |
| 68 | # Model both mean AND variance as functions of covariates |
| 69 | # Location model |
| 70 | beta_mu = pm.Normal("beta_mu", 0, 1, shape=X.shape[1]) |
| 71 | mu = pm.math.dot(X, beta_mu) |
| 72 | |
| 73 | # Scale model (log link to ensure positivity) |
| 74 | beta_sigma = pm.Normal("beta_sigma", 0, 0.5, shape=Z.shape[1]) |
| 75 | log_sigma = pm.math.dot(Z, beta_sigma) |
| 76 | sigma = pm.math.exp(log_sigma) |
| 77 | |
| 78 | y = pm.Normal("y", mu=mu, sigma=sigma, observed=y_obs) |
| 79 | ``` |
| 80 | |
| 81 | ### Common Distributional Patterns |
| 82 | |
| 83 | ```python |
| 84 | # Beta regression: both mu and phi vary with covariates |
| 85 | with pm.Model() as beta_reg: |
| 86 | # Mean model (logit link) |
| 87 | beta_mu = pm.Normal("beta_mu", 0, 1, shape=X.shape[1]) |
| 88 | mu = pm.math.sigmoid(pm.math.dot(X, beta_mu)) |
| 89 | |
| 90 | # Precision model (log link) |
| 91 | beta_phi = pm.Normal("beta_phi", 0, 0.5, shape=Z.shape[1]) |
| 92 | phi = pm.math.exp(pm.math.dot(Z, beta_phi)) |
| 93 | |
| 94 | # Reparameterize: alpha = mu * phi, beta = (1-mu) * phi |
| 95 | y = pm.Beta("y", alpha=mu * phi, beta=(1 - mu) * phi, observed=y_obs) |
| 96 | ``` |
| 97 | |
| 98 | ```python |
| 99 | # Negative binomial: mu and alpha both depend on covariates |
| 100 | with pm.Model() as nb_dist: |
| 101 | beta_mu = pm.Normal("beta_mu", 0, 1, shape=X.shape[1]) |
| 102 | mu = pm.math.exp(pm.math.dot(X, beta_mu)) |
| 103 | |
| 104 | beta_alpha = pm.Normal("beta_alpha", 0, 0.5, shape=Z.shape[1]) |
| 105 | alpha = pm.math.exp(pm.math.dot(Z, beta_alpha)) |
| 106 | |
| 107 | y = pm.NegativeBinomial("y", mu=mu, alpha=alpha, observed=y_obs) |
| 108 | ``` |
| 109 | |
| 110 | See `references/distributional.md` for more patterns. |
| 111 | |
| 112 | ## R2D2M2CP Prior |
| 113 | |
| 114 | A prior for regression coefficients that reasons about the proportion of variance explained (R2) and decomposes it across predictors. |
| 115 | |
| 116 | ```python |
| 117 | with pm.Model() as r2d2_model: |
| 118 | # R2D2M2CP: specify expected R2 and concentration |
| 119 | beta, r2 = pmx.R2D2M2CP( |
| 120 | "beta", |
| 121 | X, # Design matrix |
| 122 | y_obs, # Observed response |
| 123 | r2=0.5, # Prior mean for R2 |
| 124 | variance_concentration=None, # Equal concentration (default) |
| 125 | ) |
| 126 | mu = pm.math.dot(X, beta) |
| 127 | sigma = pm.HalfNormal("sigma", sigma=1) |
| 128 | y = pm.Normal("y", mu=mu, sigma=sigma, observed=y_obs) |
| 129 | ``` |
| 130 | |
| 131 | ### When to Use R2D2M2CP vs Horseshoe |
| 132 | |
| 133 | | Aspect | R2D2M2CP | Horseshoe | |
| 134 | |---|---|---| |
| 135 | | Interpretability | Reasons about R2 directly | Reasons about shrinkage | |
| 136 | | Sparsity | Soft shrinkage | Strong sparsity (near-zero coefficients) | |
| 137 | | Best for | Dense signals, moderate p | Sparse signals, large p | |
| 138 | | Tuning | Prior on R2 + concentration | Global/local shrinkage scales | |
| 139 | | Implementation | `pmx.R2D2M2CP()` | Manual or pmx helper | |
| 140 | |
| 141 | For general prior specification guidance and elicitation workflows, see the [prior-elicitation skill](../prior-elicitation/SKILL.md). |
| 142 | |
| 143 | ## Regularized Horseshoe Prior |
| 144 | |
| 145 | For sparse regression where most coefficients are expected to be near zero: |
| 146 | |
| 147 | ```pytho |