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#
This tutorial assumes some pre-existing knowledge of infectious disease dynamics and Python programming. Before you dive in, we recommend:
Installing Python3 (use tools like pyenv or compile and install from the release page)
Familiarity with installing and loading modules in python, and with virtual environment management (we recommend poetry)
Familiarity with the concept of a class and metaclass in python
Familiarity with Bayesian inference, and a working understanding of MCMC methods used to fit Bayesian models to data (some resources are available here, and here)
Installing pyrenew#
You’ll need to install pyrenew
using either poetry or pip. To install
pyrenew
using poetry, run the following command from within the
directory containing the pyrenew
project:
poetry install
To install pyrenew
using pip, run the following command:
pip install git+https://github.com/CDCgov/PyRenew@main
The fundamentals#
pyrenew
’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(...)
Whereas, in some other cases, we may instead use a fixed quantity for
that variable (like a pre-computed PMF), where the RandomVariable
’s
sample function could instead 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 case) or just retrieving some
quantity needed for other calculations (second case.)
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. At this stage, the Model
metaclass consist of
two model classes RtInfectionsRenewalModel
which is basic renewal
model consisting of infections and reproduction numbers and
HospitalAdmissionsModel
which includes basic renewal model and
hospital admisions. In the subsequent sections, we provide examples of
fitting each of these models.