library.econ
Health economics: the costs of a sim, disability-adjusted life years, discounting, and incremental cost-effectiveness ratios.
Used with: agents and compartments; declared and coded modules
Costs are not part of every sim, so these live in the library rather than in core Starsim: ssl.Costs adds a sim’s costs up as results, ssl.DALYs does the same for disability-adjusted life years, ssl.discount() discounts a result over time, and ssl.ICER compares the costs and effects of scenarios, after they have run or as a sim runs.
Classes
| Name | Description |
|---|---|
| Costs | The costs of a sim, from its results, with their total |
| DALYs | The disability-adjusted life years of a sim: the years lived with disability, plus the years of life lost |
| ICER | Compare the costs and effects of scenarios: incremental cost-effectiveness ratios |
Costs
library.econ.Costs(
unit_costs=None,
fixed=None,
start=None,
deliveries=True,
**kwargs,
)The costs of a sim, from its results, with their total
Each delivery made with a cost= (e.g. ss.vaccinate(..., cost=5)) already has a cost result; this adds them up, along with a cost for each event of any other result (e.g. care for each infection) and a fixed cost per unit time. Every cost is a result of this analyzer, named after where it came from, and total is their sum.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| unit_costs | dict |
the cost of each event of a result, by the result’s path, e.g. {'measles.infection': 50} for $50 of care for each infection, or {'routine.delivered': 5} for $5 a dose of a delivery made without its own cost= |
None |
| fixed | rate |
a cost per unit time that does not depend on any result, such as running a program (staff, cold chain, training), e.g. ss.peryear(1000) |
None |
| start | float / str |
when the fixed cost starts, read as any time is (see ss.connectors.to_time(): a bare number is a year if the sim has a calendar; default: the start of the sim) |
None |
| deliveries | bool |
whether to include the cost result of every module that has one |
True |
Examples
import starsim as ss
import starsim.library as ssl
vx = ss.vaccinate(efficacy=0.9, who=ss.person.age < 5, when=ss.schedule(every=ss.years(1)), coverage=0.5, cost=5)
costs = ssl.Costs(unit_costs={'sir.infection': 50}, fixed=ss.peryear(1000))
sim = ss.Sim(diseases='sir', networks='random', interventions=vx, analyzers=costs)
sim.run()
sim.results.costs.plot()
print(sim.results.costs.total.sum())Methods
| Name | Description |
|---|---|
| fill | Work out the costs of some time points |
| step | The costs so far, for what runs during the sim, such as ssl.ICER; the results are final after finalize_results() |
fill
library.econ.Costs.fill(ind=slice(None))Work out the costs of some time points
step
library.econ.Costs.step()The costs so far, for what runs during the sim, such as ssl.ICER; the results are final after finalize_results()
DALYs
library.econ.DALYs(weights=None, life_lost=None, deaths=None, **kwargs)The disability-adjusted life years of a sim: the years lived with disability, plus the years of life lost
Years lived with disability (yld) are the time people spend in a state, multiplied by the disability weight of the state. Years of life lost (yll) are the years that each person who dies of a disease would otherwise have lived. Every result is per step, and dalys is their sum.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| weights | dict |
the disability weight of a result that counts people, by its path, e.g. {'hiv.I': 0.2} |
None |
| life_lost | float |
the years of life lost for each death; by default, with agents, those left at each person’s age at death in a standard life table (from the Global Burden of Disease 2019 study). Without agents, ages at death aren’t known, so give this | None |
| deaths | list |
the paths of the results that count deaths, needed only with life_lost (default: each disease’s death) |
None |
Examples
import starsim as ss
import starsim.library as ssl
sir = ss.SIR(p_death=0.01)
sim = ss.Sim(diseases=sir, networks='random', analyzers=ssl.DALYs(weights={'sir.I': 0.1}))
sim.run()
print(sim.results.dalys.dalys.sum())Methods
| Name | Description |
|---|---|
| death_results | The paths of the results that count deaths |
| fill | Work out the years lived with disability and, given life_lost, the years of life lost, at some time points |
| step | The years of life lost by those who died in this step, and the results so far, for what runs during the sim, such as ssl.ICER |
death_results
library.econ.DALYs.death_results()The paths of the results that count deaths
fill
library.econ.DALYs.fill(ind=slice(None))Work out the years lived with disability and, given life_lost, the years of life lost, at some time points
step
library.econ.DALYs.step()The years of life lost by those who died in this step, and the results so far, for what runs during the sim, such as ssl.ICER
ICER
library.econ.ICER(
sims=None,
outcome='dalys.dalys',
cost='costs.total',
baseline=None,
discount=0,
discount_start=None,
window=None,
**kwargs,
)Compare the costs and effects of scenarios: incremental cost-effectiveness ratios
Each scenario’s cost and outcome are totaled (discounted if discount is given), and the outcome averted is that of the baseline less the scenario’s. The scenarios are ordered by cost, and each is compared with the next cheaper one that isn’t dominated. A scenario is dominated if it costs more and averts no more than a cheaper one, and extendedly dominated if a mix of two others would avert more for the same cost. With several runs of each scenario, their means are compared.
This works in two ways. After the scenarios have run, give them to ssl.ICER(), and read icer.df. Or add it to a sim as an analyzer, after ssl.Costs and ssl.DALYs: the sim then counts as one more scenario, compared at each step with the scenarios given over the same period, so that what runs in the sim can act on the comparison, for example an intervention that follows icer.best(wtp=1000).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| sims | MultiSim / dict / list |
the sims of each scenario: a run MultiSim, a dict of run sims by label, or a list of run sims | None |
| outcome | str |
the path of the result to avert (default: the total of ssl.DALYs), e.g. ‘measles.infection’ |
'dalys.dalys' |
| cost | str |
the path of the cost result (default: the total of ssl.Costs) |
'costs.total' |
| baseline | str |
the label of the scenario to compare with (default: the first) | None |
| discount | float |
the discount rate per year for both costs and outcomes (see ssl.discount()) |
0 |
| discount_start | float / str |
when discounting starts (default: the first time point) | None |
| window | dur |
in a sim, how long a time to compare, ending with the latest step (default: all of it so far); one step long compares the last step alone, e.g. window=ss.years(1) with yearly steps |
None |
Attributes
| Name | Type | Description |
|---|---|---|
| df | dataframe |
a row for each scenario, with columns cost, outcome, averted, incremental cost, incremental averted, ICER, dominated, and extended |
Examples
# After the runs
msim = ss.parallel({'Nothing': base, 'Vaccine': base.new(interventions=vx)}, n_runs=20)
icer = ssl.ICER(msim, discount=0.03, discount_start=2010)
icer.df
icer.plot()
icer.plot_ceac()
icer.best(wtp=1000)
# During a run, compared with a finished sim
icer = ssl.ICER(dict(Nothing=base.run()), window=ss.years(1))
sim = ss.Sim(..., analyzers=[ssl.Costs(), ssl.DALYs(weights=...), icer])Methods
| Name | Description |
|---|---|
| best | The scenario with the highest mean net benefit at a willingness to pay |
| collect | The cost and outcome of each scenario, as arrays of runs by time points |
| net_benefit | The net benefit of each scenario at a willingness to pay per outcome averted: a dataframe with a row for each run and a column for each scenario |
| plot | Plot the cost-effectiveness plane: each scenario’s cost against the outcome it averts, with each run, and the line through those that aren’t dominated |
| plot_ceac | Plot the cost-effectiveness acceptability curve: the fraction of runs in which each scenario is the best, by willingness to pay |
| step | Count this sim as a scenario, as it stands |
| totals | The discounted total cost and outcome of each scenario, as arrays with one value per run, by label |
best
library.econ.ICER.best(wtp)The scenario with the highest mean net benefit at a willingness to pay
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| wtp | float |
the willingness to pay per outcome averted | required |
collect
library.econ.ICER.collect(sims)The cost and outcome of each scenario, as arrays of runs by time points
net_benefit
library.econ.ICER.net_benefit(wtp)The net benefit of each scenario at a willingness to pay per outcome averted: a dataframe with a row for each run and a column for each scenario
plot
library.econ.ICER.plot(**kwargs)Plot the cost-effectiveness plane: each scenario’s cost against the outcome it averts, with each run, and the line through those that aren’t dominated
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| kwargs | dict |
passed to ss.plot_args() |
plot_ceac
library.econ.ICER.plot_ceac(wtp=None, **kwargs)Plot the cost-effectiveness acceptability curve: the fraction of runs in which each scenario is the best, by willingness to pay
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| wtp | array |
the values of willingness to pay (default: 50 values from 0 up to twice the largest ICER) | None |
| kwargs | dict |
passed to ss.plot_args() |
step
library.econ.ICER.step()Count this sim as a scenario, as it stands
totals
library.econ.ICER.totals()The discounted total cost and outcome of each scenario, as arrays with one value per run, by label
Functions
| Name | Description |
|---|---|
| discount | Discount values over time at a yearly rate |
discount
library.econ.discount(values, rate, start=None, t=None)Discount values over time at a yearly rate
Each value at time t (in years) is weighted by (1+rate)**-(t-start), so values before start are not discounted.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| values | Result / array |
a result (or the results of the runs of a MultiSim), or an array of values at the times t |
required |
| rate | float |
the discount rate per year, e.g. 0.03 | required |
| start | float / str |
when discounting starts (default: the first time point) | None |
| t | array |
the time of each value in years (default: the result’s own times) | None |
Returns
| Name | Type | Description |
|---|---|---|
The discounted values, of the same type as values (so a result can be summed, plotted, etc.) |
Examples
cost = ssl.discount(sim.results.costs.total, 0.03, start=2010).sum()