Skip to main content

Random Stuff

The SEIR Model

St+1=StβStItNEt+1=Et+βStItNσEtIt+1=It+σEtγItRt+1=Rt+γIt\begin{align*} S_{t+1} &= S_t - \beta \frac{S_t I_t}{N} \\ E_{t+1} &= E_t + \beta \frac{S_t I_t}{N} - \sigma E_t \\ I_{t+1} &= I_t + \sigma E_t - \gamma I_t \\ R_{t+1} &= R_t + \gamma I_t \end{align*}

where:

  • β\beta — transmission probability per contact
  • σ\sigma — rate of progression from exposed to infected, the E → I rate (1Incubation Period\frac{1}{\text{Incubation Period}})
  • γ\gamma — recovery rate, the I → R rate (1Infectious Period\frac{1}{\text{Infectious Period}})
  • R0=βγR_0 = \frac{\beta}{\gamma}
  • N=S+E+I+RN = S + E + I + R

Sample Code that Implements the Model

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

def simulate_seir(
S0,
E0,
I0,
R0,
beta_mean,
beta_sd,
sigma,
gamma,
days,
stochastic_beta=True,
waning_immunity=False,
waning_interval=60,
waning_fraction=0.10,
seed=42,
):
"""Simulate a discrete-time SEIR model.

Parameters
----------
S0, E0, I0, R0 : float
Initial susceptible, exposed, infectious, and recovered counts.
beta_mean : float
Mean transmission probability/rate.
beta_sd : float
Standard deviation for stochastic beta.
sigma : float
Progression rate from exposed to infectious: 1/incubation period.
gamma : float
Recovery/removal rate: 1/infectious period.
days : int
Number of days to simulate.
stochastic_beta : bool
If True, draw beta_t ~ Normal(beta_mean, beta_sd) each day.
waning_immunity : bool
If True, move waning_fraction of R back to S every waning_interval days.
"""
rng = np.random.default_rng(seed)
N = S0 + E0 + I0 + R0

S = np.zeros(days + 1)
E = np.zeros(days + 1)
I = np.zeros(days + 1)
R = np.zeros(days + 1)
beta_values = np.zeros(days + 1)

S[0], E[0], I[0], R[0] = S0, E0, I0, R0
beta_values[0] = beta_mean

for t in range(days):
if stochastic_beta:
beta_t = rng.normal(beta_mean, beta_sd)
beta_t = max(beta_t, 0) # prevent biologically impossible negative transmission
else:
beta_t = beta_mean
beta_values[t + 1] = beta_t

new_exposed = beta_t * S[t] * I[t] / N
new_infectious = sigma * E[t]
new_recovered = gamma * I[t]

S[t + 1] = S[t] - new_exposed
E[t + 1] = E[t] + new_exposed - new_infectious
I[t + 1] = I[t] + new_infectious - new_recovered
R[t + 1] = R[t] + new_recovered

# Every 60 days, 10% of recovered individuals lose immunity and return to S.
# This is applied after disease-state transitions for that day.
if waning_immunity and (t + 1) % waning_interval == 0:
waned = waning_fraction * R[t + 1]
R[t + 1] -= waned
S[t + 1] += waned

return pd.DataFrame({
"day": np.arange(days + 1),
"S": S,
"E": E,
"I": I,
"R": R,
"beta": beta_values,
})

def summarize_epidemic(results, infection_threshold=1):
"""Return peak day, peak infectious count, and approximate epidemic duration."""
peak_idx = results["I"].idxmax()
peak_day = int(results.loc[peak_idx, "day"])
peak_infected = results.loc[peak_idx, "I"]

active = results[results["I"] >= infection_threshold]
if len(active) == 0:
duration = 0
else:
duration = int(active["day"].max() - active["day"].min() + 1)

return peak_day, peak_infected, duration


def plot_seir(results, title):
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(results["day"], results["S"], label="Susceptible")
ax.plot(results["day"], results["E"], label="Exposed")
ax.plot(results["day"], results["I"], label="Infectious")
ax.plot(results["day"], results["R"], label="Recovered/Removed")
ax.set_xlabel("Day")
ax.set_ylabel("Number of individuals")
ax.set_title(title)
ax.legend()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()

Other Notes

Modeling Vaccines

  • Vaccines would reduce the number of susceptible people.
  • You can change beta directly.

S → beta → E → gamma → I → lambda → R

If you are vaccinated you blast from S → R.

So,

St+1=StNew ExposuresNew VaccinationsRt+1=Rt+New Exposures+New VaccinationsS_{t+1} = S_t - \text{New Exposures} - \text{New Vaccinations} \\ R_{t+1} = R_t + \text{New Exposures} + \text{New Vaccinations}
  • If R’s dont get boosted they go back to S.
  • If they do, they stay in R.

Tracking Atomic statuses is hard!

“Endemic Equilibrium”

Rt=R0×StNR_t = R_0 \times \frac{S_t}{N}

How would you eval if a vaccine intervention was effective?

Run the models, Plot Infectious curves.

Beta is constant, transmission is stable.

If transmission is volatile, if it swings, vaccines won’t be as effective. What do you do? When building a dam, don’t plan for average flow, plan for storm flow — this is how to think about vaccines.

SEER - Surveillance, Epidemiology, and End Results

SEER

SEER collects cancer incidence data from population-based cancer registries covering approximately 45.9 percent of the U.S. population. The SEER registries collect data on patient demographics, primary tumor site, tumor morphology, stage at diagnosis, and first course of treatment, and they follow up with patients for vital status.

Complete Case Analysis

Don’t use any missing data.

Missingness Indicators

Might be better than imputation since you’re capturing the clinical signal?

Immortal Time Bias

This is a weird one!