CDCgov / CDCgov/PyRenew

Add "RatioLinkedAscertainment" component

Open
#852 0 comments 0 reactions 1 assignee View on GitHub

@cdc-mitzimorris is already working on this.

Since Jul 10, 2026.

model
Dominant language
Python
Stars
29
Forks
9
Avg merge
9d 9h
Merged PRs (30d)
8

Description

Add a new class "RatioLinkedAscertainment" which relates the ascertainment rate of two observation processes where the ascertainment rate for one process is the base rate and the ascertainment rate for the other is some ratio of the two signals.

This follows pyrenew-hew which parameterizes the ascertainment for the ED visits and hospitalizations as a base rate (IEDR) and a multiplier (IHR / IEDR).

This can be implemented straightforwardly using the base Ascertainment component:

"""Linked ascertainment components."""

from __future__ import annotations

from collections.abc import Mapping

import jax
import numpyro
from pyrenew.ascertainment import AscertainmentModel
from pyrenew.metaclass import RandomVariable


class RatioLinkedAscertainment(AscertainmentModel):
    """Two ascertainment rates expressed as a base rate and a ratio."""

    def __init__(
        self,
        name: str,
        base_signal: str,
        linked_signal: str,
        base_rate_rv: RandomVariable,
        ratio_rv: RandomVariable,
    ) -> None:
        """Create a linked ascertainment model for two named signals."""
        super().__init__(name=name, signals=(base_signal, linked_signal))
        self.base_signal = base_signal
        self.linked_signal = linked_signal
        self.base_rate_rv = base_rate_rv
        self.ratio_rv = ratio_rv

    def sample(self, **kwargs: object) -> Mapping[str, jax.Array]:
        """Sample the base rate and ratio, then compute the linked rate."""
        base_rate = self.base_rate_rv()
        ratio = self.ratio_rv()
        linked_rate = base_rate * ratio
        numpyro.deterministic(f"{self.name}_{self.base_signal}", base_rate)
        numpyro.deterministic(f"{self.name}_{self.linked_signal}", linked_rate)
        return {self.base_signal: base_rate, self.linked_signal: linked_rate}

The corresponding Python code to specify a model using the PyrenewBuilder class is:

iedr_rv = DistributionalVariable(name="iedr",  dist.LogNormal(0.0, jnp.log(jnp.sqrt(2.0))))

ratio_rv = DistributionalVariable(name="ihr_rel_iedr",  dist.LogNormal(0.0, jnp.log(jnp.sqrt(2.0))))

ascertainment = RatioLinkedAscertainment(
    name="he_ascertainment",
    base_signal="ed_visits",
    linked_signal="hospital",
    base_rate_rv=iedr_rv,
    ratio_rv=ratio_rv,
)

hospital_obs = PopulationCounts(
    name="hospital",
    ascertainment_rate_rv=ascertainment.for_signal("hospital"),
    ...
)

ed_obs = PopulationCounts(
    name="ed_visits",
    ascertainment_rate_rv=ascertainment.for_signal("ed_visits"),
    ...
)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.