Surgical — PyMC cross-check (intercept-only binomial GLMM)¶
Companion to surgical_python.ipynb. pm.Binomial with a non-centred hospital random intercept; NUTS. Checks the partial-pooled hospital rates against the from-scratch Seeds engine.
In [1]:
import numpy as np, pandas as pd, pymc as pm, pytensor.tensor as pt, arviz as az
from scipy.special import expit
from seeds_gibbs import seeds_gibbs
d = pd.read_csv('surgical.csv'); r = d['r'].to_numpy(); n = d['n'].to_numpy(); I = len(d)
with pm.Model() as m:
mu = pm.Normal('mu', 0, 10); sigma = pm.HalfNormal('sigma', 1.0)
z = pm.Normal('z', 0, 1, shape=I); b = pm.Deterministic('b', sigma*z)
p = pm.Deterministic('p', pm.math.invlogit(mu + b))
pm.Binomial('r', n=n, p=p, observed=r)
idata = pm.sample(2000, tune=2000, chains=4, target_accept=0.95, random_seed=1, progressbar=False)
po = idata.posterior; P_pm = po['p'].values.reshape(-1, I)
print('PyMC: population rate %.1f%% sigma %.3f max r_hat %.3f'
% (100*expit(po['mu'].values).mean(), float(po['sigma'].mean()), float(az.summary(idata,var_names=['mu','sigma'])['r_hat'].max())))
g = seeds_gibbs(r.astype(float), n.astype(float), np.ones((I,1)), R=30000, burn=8000, seed=1)
P_fs = expit(g['beta'][:,0][:,None] + g['bdraws'])
print('\nhosp raw% PyMC% from-scratch%')
for i in range(I): print(' %2d %5.1f %6.1f %12.1f' % (i+1, 100*r[i]/n[i], 100*P_pm[:,i].mean(), 100*P_fs[:,i].mean()))
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [mu, sigma, z]
Sampling 4 chains for 2_000 tune and 2_000 draw iterations (8_000 + 8_000 draws total) took 5 seconds.
PyMC: population rate 7.2% sigma 0.445 max r_hat 1.000
hosp raw% PyMC% from-scratch% 1 0.0 5.1 5.8 2 12.2 10.6 9.9 3 6.7 7.0 7.2 4 5.7 5.9 6.1 5 3.8 5.0 5.6 6 6.6 6.9 7.1 7 6.1 6.6 6.9 8 14.4 12.6 11.6 9 6.8 7.0 7.1 10 8.2 7.9 7.9 11 11.3 10.4 9.8 12 6.7 6.8 7.0
Results¶
PyMC reproduces the from-scratch shrunk hospital rates (hospital 1: 0% → ~6%; H8 → ~11–12%), with population rate ≈ 7.2% and σ ≈ 0.45, r̂ ≈ 1.0. Same partial-pooling, sampled by NUTS.