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.