Nowcasting Concepts - Delay Hazard Models and the Renewal Equation
Summary
Concept note, no raw source. It writes the reporting-delay model as a discrete hazard (the Hoehle & an der Heiden 2014 route that McGough compares against) with weekday and drift effects, shows why truncated data fake a “wave is ending”, and expresses the renewal equation as a triangular recursion so that can be a latent series fitted jointly with the nowcast. These are the ingredients of PyMC examples E38 and E90. Literature: Hoehle & an der Heiden (2014, ‘Bayesian nowcasting during the STEC O104:H4 outbreak in Germany, 2011’, Biometrics 70(4): 993-1002); Guenther et al. (2021, Biom J, Bavaria COVID-19 nowcasts); Cori et al. (2013, AJE 178(9)); Fraser (2007).
Overview
E38: German COVID-19 hospitalisation triangle (RKI, Hospitalization Nowcast Hub) with real as-of-then snapshots. E90: a hierarchical negative-binomial hazard nowcast across 16 states used to decide whether a hospital threshold is crossed (“call it today or wait?”) with decision curves.
Main Content
Discrete delay hazard
For onset day and delay , the hazard . The delay pmf is and the reporting CDF . Model -type drift terms (reporting-weekday effect and a slow weekly drift).
Cell likelihood
, , with a random walk (or GP). Observed cells satisfy ; the nowcast is (observed part plus posterior-predictive for the future cells). Compare multiplicative NobBS: constant (McGough 2020 - NobBS Model and Priors). Hierarchical version (E90): state-level with partial pooling of across states (Hierarchical Models).
Spurious "wave ending"
If grows at rate and reporting CDF is at lag , the reported count for onset day is . The ratio of the latest day’s reported count to day ‘s is , which is below 1 whenever ; so reported counts can decline in the last days even in exponential growth. Nowcasting divides by (inflating by ) to remove it; using as-of-then snapshots rather than a final preprocessed file is the only honest backtest.
Renewal equation as triangular solve
(with susceptible depletion ignored) is lower-triangular in : given seeds and , each follows from earlier ones, i.e. . Place on a random walk, observe through the delay hazard model, and fit and the nowcast jointly (so uncertainty in the latest incidence flows into ). Generation interval is supplied externally (Gostic 2020 - Generation Interval and the Renewal Equation).
Decision use (E90)
Declare “threshold crossed” when . Choose by net benefit across thresholds (decision curve) rather than a fixed 0.5; waiting reduces false alarms but costs time. See Vickers 2019 - Net Benefit and Threshold Probability.
Examples
Exponential growth check
/day, for lags . Reported ratio of today to 3 days ago instead of the true : the series looks like a 73% crash. Dividing each day by its gives back 1.35.
import numpy as np
def renewal(R, w, seed):
S=len(w); I=list(seed)
for t in range(len(seed), len(R)):
I.append(R[t]*sum(w[s-1]*I[t-s] for s in range(1,min(S,t)+1)))
return np.array(I)
w=np.array([.1,.3,.3,.2,.1])
I=renewal(np.full(60,1.2), w, [10]*5) # R=1.2 -> growth# PyMC sketch (untested): hazard nowcast
import pymc as pm, pytensor.tensor as pt
with pm.Model():
a = pm.Normal("a", 0, 1.5, shape=D+1)
gam = pm.ZeroSumNormal("gam", sigma=0.3, shape=7)
h = pm.math.sigmoid(a[d_idx] + gam[wd_idx])
# p_d = h_d * prod_{k<d}(1-h_k): build via cumulative sum of log(1-h) over d for each t
...
N = pt.exp(pm.GaussianRandomWalk("alpha", sigma=pm.HalfNormal("s",.2), shape=T))
pm.NegativeBinomial("n", mu=N[t_idx]*p[obs], alpha=pm.Exponential("phi",.1), observed=n_obs)Connections
For biases from observation delay and truncation see Gostic 2020 - Rt Estimation Pipeline and Right-Truncation Bias. Survival analysis perspective: Survival Analysis (reverse-time hazard = hazard in the time-to-report).