Calculating derived outputs

In the previous example, we saw how to create and run a compartmental model.

This example shows you how you can request more detailed outputs from the model, in addition to just the compartment sizes. Summer supports the calculation of “derived outputs”: these are additional outputs that are calculated from either:

  • the model compartment sizes for each timestep; or

  • the model flow rates at each timestep

There are several different types of derived outputs that will be presented in this example:

To start, let’s define some utility functions to create a SIR model that is similar to the one from the last example:

[1]
from summer2 import CompartmentalModel
import pandas as pd
pd.options.plotting.backend = "plotly"

def build_model():
    """Returns a new SIR model"""
    model = CompartmentalModel(
        times=[0, 20],
        compartments=["S", "I", "R"],
        infectious_compartments=["I"],
        timestep=0.1,
    )
    model.set_initial_population(distribution={"S": 990, "I": 10})
    model.add_infection_frequency_flow(name="infection", contact_rate=2, source="S", dest="I")
    model.add_transition_flow(name="recovery", fractional_rate=1/3, source="I", dest="R")
    model.add_death_flow(name="infection_death", death_rate=0.05, source="I")
    return model

Let’s quickly visualize what the compartments are doing over time:

[2]
model = build_model()
model.run()

model.get_outputs_df().plot()

Requesting derived outputs

We can ask a model to calculate extra outputs that are derived from the compartment sizes and flow rates. For example, we might want to ask the model to track the number of people who died from infection per timestep.

[3]
# Create a model (see above)
model = build_model()

# Request that the model calculate a derived output when it is run.
model.request_output_for_flow(name="deaths", flow_name="infection_death")
[3]:
DerivedOutput deaths {'request_type': 'flow', 'flow_name': 'infection_death', 'source_strata': {}, 'dest_strata': {}, 'raw_results': False, 'save_results': True}

Now when we run the model, the infections deaths will be available in a DataFrame that we can access via model.get_derived_outputs_df(). The raw values for these are also available as a dictionary of numpy arrays via model.derived_outputs

[4]
# Run the model
model.run()

# View the derived outputs dictionary that we calculated when `run()` was called.
model.get_derived_outputs_df()
[4]:
deaths
0.0 0.500000
0.1 0.543221
0.2 0.636979
0.3 0.746548
0.4 0.874461
... ...
19.6 0.104050
19.7 0.100225
19.8 0.096541
19.9 0.092991
20.0 0.089573

201 rows × 1 columns

Flow outputs

A flow output tracks a set of requested flow rates for each timestep. These requests can also select flows between particular strata in a stratified model (see later examples).

[5]
model = build_model()

# Request that the 'infection_death' flow is tracked as a derived output named 'deaths'.
model.request_output_for_flow(name="deaths", flow_name="infection_death")

model.run()
model.get_derived_outputs_df().plot()

Cumulative outputs

You can use a cumulative output to request that the model tracks the cumulative sum of other derived outputs over time. For example, let’s track total infection deaths and the total people recovered:

[6]
model = build_model()
model.request_output_for_flow(name="deaths", flow_name="infection_death")

# Request that the 'deaths' derived output is accumulated into 'deaths_cumulative'.
model.request_cumulative_output(name="deaths_cumulative", source="deaths")

model.run()
model.get_derived_outputs_df().plot()

Compartment outputs

A compartment output tracks the sum of one or more compartments at each timestep. These requests can also select compartments for particular strata in a stratified model (see later examples).

[7]
model = build_model()

# Request that the S and R compartment sizes are combined into 'uninfected'.
model.request_output_for_compartments(name="uninfected", compartments=["S", "R"])

model.run()
model.get_derived_outputs_df().plot()

Aggregate outputs

You can use an aggregate output to request an aggregate of other derived outputs.

[8]
model = build_model()

# Track some flows.
model.request_output_for_flow(name="deaths", flow_name="infection_death")
model.request_output_for_flow(name="recoveries", flow_name="recovery")

# Accumulate the flows.
model.request_cumulative_output(name="deaths_cumulative", source="deaths")
model.request_cumulative_output(name="recoveries_cumulative", source="recoveries")

# Aggregate 'deaths_cumulative' and 'recovered_cumulative' into a single output.
model.request_aggregate_output(
    name="dead_or_recovered_cumulative",
    sources=["deaths_cumulative", "recoveries_cumulative"]
)

model.run()
model.get_derived_outputs_df().plot()
# (In this simple model, this could be also easily be tracked as the complement of the the susceptible population.)

Function outputs

You can use function outputs to store the value of any computegraph Function object, including accessing previously requested derived outputs via the DerivedOutput constructor.

For example, here we request a calculation that gets us the prevalence of the disease.

[9]
from summer2.parameters import Function, DerivedOutput
[10]
model = build_model()

# Track the number of infectious people as a derived output.
# Here we use `save_results=False` because although we need this as an intermediary value to
# calculate results, we do not want to store the full series
model.request_output_for_compartments(name="count_infectious", compartments=["I"], save_results=False)

# Track the total population as a derived output.
model.request_output_for_compartments(name="total_population", compartments=["S", "I", "R"], save_results=False)

# Construct a computegraph Function
infectious_prevalence = DerivedOutput("count_infectious") / DerivedOutput("total_population")

# Request a function output, using our constructed Function object
model.request_function_output(
    name="prevalence",
    func=infectious_prevalence
)

model.run()
model.get_derived_outputs_df().plot()

Summary

That’s it for now, now you know how to:

  • Request derived outputs

  • Chain and combine derived outputs

  • Access and visualize the derived outputs

A detailed API reference of the CompartmentalModel class can be found here

[ ]