Getting started with pyrenew

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.