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:
Flow output: A sum of one or more flow rates at each timestep
Compartment output: A sum of one or more compartment sizes at each timestep
Aggregate output: An aggregate of other derived outputs
Cumulative output: A cumulative sum of another derived output
Function output: Output the value of computegraph Function
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()