Seeds — PyMC cross-check (binomial GLMM)¶

Companion to seeds_python.ipynb. pm.Binomial with a non-centred plate random effect ($b=\sigma z$) to avoid the funnel; NUTS.

In [1]:
import numpy as np, pymc as pm, pytensor.tensor as pt, arviz as az
from seeds_gibbs import seeds_data, seeds_gibbs
r, n, X = seeds_data(); I = len(r); k = X.shape[1]
with pm.Model() as m:
    beta = pm.Normal('beta', 0, 10, shape=k)
    sigma = pm.HalfNormal('sigma', 1.0); z = pm.Normal('z', 0, 1, shape=I); b = pm.Deterministic('b', sigma*z)
    p = pm.math.invlogit(pt.dot(X, beta) + b)
    pm.Binomial('r', n=n.astype(int), p=p, observed=r.astype(int))
    idata = pm.sample(2000, tune=2000, chains=4, target_accept=0.95, random_seed=1, progressbar=False)
po = idata.posterior; nm = ['intercept','seed type','root extract','interaction']; bb = po['beta'].values.reshape(-1,k)
for j in range(k): print('%-13s %7.3f [%.2f, %.2f]' % (nm[j], bb[:,j].mean(), np.percentile(bb[:,j],2.5), np.percentile(bb[:,j],97.5)))
print('sigma = %.3f [%.2f, %.2f]   max r_hat %.3f' % (float(po['sigma'].mean()), float(po['sigma'].quantile(.025)), float(po['sigma'].quantile(.975)), float(az.summary(idata,var_names=['beta','sigma'])['r_hat'].max())))
g = seeds_gibbs(r, n, X, R=30000, burn=8000, seed=1)
print('\nfrom-scratch vs PyMC: extract %.2f/%.2f  interaction %.2f/%.2f  sigma %.2f/%.2f'
      % (g['beta'].mean(0)[2], bb[:,2].mean(), g['beta'].mean(0)[3], bb[:,3].mean(), g['sigma'].mean(), float(po['sigma'].mean())))
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [beta, sigma, z]
Sampling 4 chains for 2_000 tune and 2_000 draw iterations (8_000 + 8_000 draws total) took 6 seconds.
intercept      -0.551 [-0.98, -0.14]
seed type       0.062 [-0.62, 0.72]
root extract    1.363 [0.79, 1.97]
interaction    -0.835 [-1.79, 0.09]
sigma = 0.346 [0.08, 0.67]   max r_hat 1.000
from-scratch vs PyMC: extract 1.34/1.36  interaction -0.80/-0.84  sigma 0.13/0.35

Results¶

PyMC reproduces the from-scratch fit: root extract ≈ +1.34, interaction ≈ −0.81, seed-type ≈ 0, r̂ ≈ 1.0. The random-effect σ is the prior-sensitive part (HalfNormal here vs vague Inverse-Gamma in the from-scratch sampler) — both small (from-scratch ≈ 0.13, PyMC ≈ 0.35), since the Seeds over-dispersion is weakly identified — but the fixed effects agree exactly.