Getting started with pyrenew
pyrenew
is a flexible tool for simulating and making statistical
inferences from epidemiologic models, with an emphasis on renewal
models. Built on numpyro
, pyrenew
provides core components for model
building and pre-defined models for processing various observational
processes.
Prerequisites
The following tutorials assume that you are familiar with both infectious disease dynamics and Python programming.
- You must have a Python3 installation (use tools like pyenv or compile and install from the release page).
- You should be able to install packages, load modules in python, and manage virtual environments (we recommend uv).
- You should understand the concept of a class and metaclass in python.
- You should be familiar with Bayesian inference and have a working understanding of MCMC methods used to fit Bayesian models to data (some resources are available here, and here).
Installing pyrenew
You can install pyrenew
using either uv or pip. To install pyrenew
using uv
, run the following command from within the directory
containing the pyrenew
project:
uv sync
To install pyrenew
using pip, run the following command:
pip install git+https://github.com/CDCgov/PyRenew@main
The fundamentals
The pyrenew
package leverages numpyro
’s flexibility to build models
via composition. It’s core components are the metaclasses
RandomVariable
and Model
(in Python, a metaclass is a class whose
instances are also classes, where a class is a template for making
objects). Within the pyrenew
package, a RandomVariable
is a quantity
that models can estimate and sample from, including deterministic
quantities. The benefit of this design is that the definition of the
sample()
function can be arbitrary, allowing the user to either sample
from a distribution using numpyro.sample()
, compute fixed quantities
(like a mechanistic equation), or return a fixed value (like a
pre-computed PMF.) For instance, when estimating a PMF, the
RandomVariable
sampling function may roughly be defined as:
# define a new class called MyRandVar that inherits from the RandomVariable class
class MyRandVar(RandomVariable):
#define a method called sample that returns an object of type ArrayLike
def sample(...) -> ArrayLike:
# calls sample function from NumPyro package
return numpyro.sample(...)
We can instead use a fixed quantity for that variable (like a
pre-computed PMF), where the RandomVariable
’s sample function could be
defined as:
# instead define MyRandVar to still inherit from the RandVariable class
class MyRandVar(RandomVariable):
#define sample method that still returns an ArrayLike object
def sample(...) -> ArrayLike:
#sampling method is a pre-computed PMF, a JAX NumPy array with explicit elements
return jax.numpy.array([0.2, 0.7, 0.1])
Thus, when a Model
samples from MyRandVar
, it could be either adding
random variables to be estimated (first example) or just retrieving some
quantity needed for other calculations (second example).
The Model
metaclass provides basic functionality for estimating and
simulation. Like RandomVariable
, the Model
metaclass has a
sample()
method that defines the model structure. Ultimately, models
can be nested (or inherited), providing a straightforward way to add
layers of complexity. Currently the Model
metaclass contains of two
model classes
RtInfectionsRenewalModel
- a basic renewal model consisting of infections and reproduction numbers.HospitalAdmissionsModel
- a model which extends a basic renewal model to account for latent and observed hospital admissions.