Philip 2020 - Bayesian GEV Attribution in PyMC

Summary

The protocol’s frequentist recipe (penalised ML + bootstrap) translates to a Bayesian non-stationary GEV with a weakly informative shape prior replacing the penalty. , FAR, return levels and become posterior functionals. The main computational hazard is the support wall: when every observation must lie below the endpoint, which makes NUTS diverge unless the model is re-parameterised. Code is untested.

Overview

Concept note, not in the paper. It follows E37 (Pacific Northwest heatwave 2021) where annual maxima at Sea-Tac and Portland and GISTEMP GMST are used, with pymc_extras.GenExtreme checked against scipy.

Main Content

Non-stationary GEV with shift fit

, ( = smoothed GMST anomaly), constant (Philip et al. 2020, Sec. 4.3.1). Posterior functionals: , , , .

Priors. (weakly informative on the data scale); - on the log scale if desired; with units of the variable per degree; geophysical shape prior replacing the paper’s penalty (Katz et al. 2002): Martins and Stedinger (2000) use on (a standard hydrology prior, not from this paper).

PyMC 5 sketch (untested; verify the sign convention of xi in pymc_extras.GenExtreme)

import numpy as np, pymc as pm, pytensor.tensor as pt
from pymc_extras.distributions import GenExtreme     # check: shape sign vs scipy c = -xi
 
with pm.Model() as gev_shift:
    T = pm.Data("T", T_anom)                         # smoothed GMST anomalies
    mu0 = pm.Normal("mu0", x.mean(), x.std())
    sigma = pm.HalfNormal("sigma", x.std())
    xi_raw = pm.Beta("xi_raw", 6, 9)
    xi = pm.Deterministic("xi", xi_raw - 0.5)        # (-0.5, 0.5), mean about -0.1
    alpha = pm.Normal("alpha", 0, 3)
    mu = mu0 + alpha * T
    GenExtreme("y", mu=mu, sigma=sigma, xi=xi, observed=x)
    idata = pm.sample(2000, tune=2000, target_accept=0.95)
 
# attribution functionals from posterior draws
from scipy import stats
post = idata.posterior.stack(s=("chain", "draw"))
def p_exceed(Tval):
    c = -post["xi"].values
    return stats.genextreme.sf(x_event, c, loc=post["mu0"].values + post["alpha"].values * Tval,
                               scale=post["sigma"].values)
PR = p_exceed(T1) / p_exceed(T0)
print(np.median(PR), np.percentile(PR, [2.5, 97.5]))

Removing the support wall. For the upper endpoint is and every observation must satisfy . Leaving the observed-data logp to return outside the support creates a cliff for NUTS. Re-parameterise through the endpoint instead: for the shift fit define the detrended maximum and sample an excess so that is the endpoint of the base distribution; then (for ), with . Then support is satisfied by construction for and the sampler explores smoothly (E37’s “reparameterisation that removes support-wall divergences”); positive has no upper wall but needs (lower wall).

Trigger-event handling. The paper excludes the trigger event from the fit but requires non-zero probability. Bayesian analogue: fit without it, then confirm across draws; or include it as a sensitivity run (shows how much the “dice loaded” estimate rises when the record is included - selection bias).

Diagnostics. Return-level plot with posterior bands; q-q of probability-integral-transformed observations; prior sensitivity of (tail inference is dominated by the prior for short records); compare using GMST shift vs scale fit.

Examples

Heat-event numbers to expect

Annual maxima of daily temperature typically have (bounded). When the event is near the endpoint the posterior of is heavy-tailed or includes draws - report as “extremely unlikely in the counterfactual” and the lower bound of (Philip et al. 2020: emphasise the more stable lower bound).

Connections

Prior Predictive Checking for , Hierarchical Models (pooling several stations’ ), Posterior Predictive Checking.

See Also

Philip 2020 - Extreme Event Attribution Protocol - Overview