Rachel Oughton
24/02/2023
In this workshop, we will
heemod
and learn how to build a
Markov modelMarkov models are a type of cohort simulation:
They are often used for long-term modelling of chronic conditions.
We will demonstrate using a generic example with three states:
healthy, ill and dead.
In a Markov model, there are a finite number of health states
Each health state is associated with
The model [usually] starts with everyone in the healthiest
state
At each cycle:
The probabilities of transitioning between states are arranged in a transition matrix.
A possible transition matrix for our simple model is
## healthy ill dead
## healthy 0.97 0.02 0.01
## ill 0.00 0.96 0.04
## dead 0.00 0.00 1.00
Note that
We can represent the model graphically using the information in the transition matrix
Our simple model.
## healthy ill dead
## healthy 0.97 0.02 0.01
## ill 0.00 0.96 0.04
## dead 0.00 0.00 1.00
The transition probabilities do not reflect how long someone has been in their state, but only which state they are in.
This memoryless behaviour is known as the Markov property.
Let’s say our model begins with a cohort of 1000 healthy people.
Therefore at time \(t=1\) we have
## healthy ill dead
## 1 1000 0 0
To calculate the populations in each state at time 2, we multiply the populations at time 1 by the transition matrix:
\[ \begin{pmatrix} 1000 & 0 & 0 \end{pmatrix} \begin{pmatrix} 0.97 & 0.02 & 0.01 \\ 0 & 0.96 & 0.04 \\ 0 & 0 & 1 \end{pmatrix} = \begin{pmatrix} 970 & 20 & 10 \end{pmatrix} \]
That is, at time 2 we expect to have
- 970 of the 1000 people still in the healthy state,
- 20 in the ill state
- 10 in the dead state
## healthy ill dead
## 1 1000 0 0
## 2 970 20 10
We can carry this on for many cycles, each time multiplying the state population vector from the previous time by the transition matrix.
In Markov models, a cycle is usually a fairly long period of time, commonly a year.
## healthy ill dead
## 1 1000.0000 0.00000 0.00000
## 2 970.0000 20.00000 10.00000
## 3 940.9000 38.60000 20.50000
## 4 912.6730 55.87400 31.45300
## 5 885.2928 71.89250 42.81469
## 6 858.7340 86.72266 54.54332
## 7 832.9720 100.42843 66.59956
## 8 807.9828 113.07073 78.94642
## 9 783.7434 124.70756 91.54908
## 10 760.2311 135.39413 104.37482
Because there is a non-zero probability of remaining in each state, the system will never reach \(\left(0,0,N\right)\)
We therefore need to employ a stopping criterion to choose the number of cycles
We can combine
to generate total cost and QALYs for the cohort.
For our example, let’s say our costs and QALYs are
## State Cost QALY
## 1 healthy 40 0.95
## 2 ill 1000 0.60
## 3 dead 0 0.00
For the first cycle we have 1000 people all in the ‘healthy’ state.
Therefore over that cycle the cohort generate
- a cost of \(1000 \times{40} = 40000\) (in some monetary unit)
- an effect of \(1000\times{0.95}=950\) QALYs.
## costs QALYs
## 1 40000 950
Use matrix multiplication again to calculate the costs and QALYs at each cycle.
For example, for cycle 2:
\[ \begin{pmatrix} 970 & 20 & 10 \end{pmatrix} \begin{pmatrix} 40 & 0.95 \\ 1000 & 0.6 \\ 0 & 0 \end{pmatrix} = \begin{pmatrix} 58800 & 933.5 \end{pmatrix} \] We can work through our cohort simulation outcome to find the costs and QALYs for each cycle.
## costs QALYs
## 1 40000.00 950.0000
## 2 58800.00 933.5000
## 3 76236.00 917.0150
## 4 92380.92 900.5638
## 5 107304.21 884.1637
## 6 121072.02 867.8309
## 7 133747.31 851.5805
## 8 145390.05 835.4261
## 9 156057.30 819.3807
## 10 165803.37 803.4560
By
we can find the
heemod
In this workshop we will use the package heemod
to
specify and run Markov models.
In this section:
heemod
heemod
A lot of working with heemod
is using the
right name in the right place!
heemod
First of all we define each state in terms of its cost and effect.
# define costs and outcomes associated with each state
# We will use QALYs, but could use something different
healthy_state = define_state(
cost = 40,
QALYs = 0.95
)
ill_state = define_state(
cost = 1000,
QALYs = 0.6
)
dead_state = define_state(
cost = 0,
QALYs = 0
)
and the transition matrix:
heemod
: running the modelWe now collect the information into a strategy and run the model.
The default is to have 1000 people starting in the first state (‘healthy’ in our case):
## the strategy combines the transition matrix with the information about each state
strat_simp = define_strategy(
transition = mm_simp_heemod,
healthy = healthy_state, #[<name in transition matrix> = <defined state>]
ill=ill_state,
dead=dead_state
)
We can now run this model with the information in the strategy for some number of cycles
We link the effect and cost arguments to the relevant parts of our state definitions.
heemod
: outputThe output is formatted long, so it isn’t so easy to read
(but it is good for ggplot2
)
## .strategy_names markov_cycle state_names count
## 1 sim 1 healthy 950.0000
## 2 sim 2 healthy 855.0000
## 3 sim 3 healthy 769.5000
## 4 sim 4 healthy 692.5500
## 5 sim 5 healthy 623.2950
## 6 sim 6 healthy 560.9655
## markov_cycle .strategy_names value_names value
## 1 1 sim cost 78000.0
## 2 2 sim cost 145400.0
## 3 3 sim cost 197036.0
## 4 4 sim cost 235567.3
## 5 5 sim cost 263257.2
## 6 6 sim cost 282028.6
heemod
: plotting outputWe can plot the state populations and the costs and effects over time
The reason for building a Markov model in health economics is to assess which treatment is the most cost-effective.
We will now
heemod
functions.Suppose we are performing a cost-effectiveness analysis for two treatments of a chronic disease
‘drug A’: the current standard
‘drug B’: a newer, more effective but more costly treatment
Our model has three states:
‘mild’, ‘severe’ and ‘dead’
there is no returning from ‘severe’ to ‘mild’.
The transition matrix for drug A is
\[\begin{pmatrix} 0.75& 0.23 & 0.02\\ 0 & 0.9 & 0.1\\ 0 & 0 & 1 \end{pmatrix}\]
We can specify this in R as follows:
tm_drugA = define_transition(
state_names = c("mild", "severe", "dead"),
0.75, 0.23, 0.02,
0, 0.9, 0.1,
0, 0, 1
)
tm_drugA
## A transition matrix, 3 states.
##
## mild severe dead
## mild 0.75 0.23 0.02
## severe 0.9 0.1
## dead 1
It is thought that while on drug B, the probability of moving to a worse state is generally less.
We represent this by a parameter
Relative risk
The probability of transitioning from one state to a worse state with drug B is given by the relative risk \((rr)\) multiplied by the probability of the same transition with drug A.
So, in our example:
\[TM_A =\begin{pmatrix} 0.75& 0.23 & 0.02\\ 0 & 0.9 & 0.1\\ 0 & 0 & 1 \end{pmatrix}\]
Transition matrix: drug B
\[TM_B = \begin{pmatrix} C& 0.23rr & 0.02rr\\ 0 & C & 0.1rr\\ 0 & 0 & 1 \end{pmatrix}\]
Where \(C\) is the complement - the value necessary for the probabilities to sum to one.
heemod
In the code below
rr
is our relative riskC
stands for complement, and is one minus
the sum of the other probabilities in that row (ensuring the row
sums to one).rr = 0.7 ## Our relative risk
tm_drugB = define_transition(
state_names = c("mild", "severe", "dead"),
C, 0.23*rr, 0.02*rr,
0, C, 0.1*rr,
0, 0, 1
)
tm_drugB
## A transition matrix, 3 states.
##
## mild severe dead
## mild C 0.23 * rr 0.02 * rr
## severe C 0.1 * rr
## dead 1
In this example we will split the costs of drugs and
healthcare.
We will have to specify costs for each
drug (strategy)
The function
dispatch_strategy
specifies a different return value for each strategy (each drug, in this example).
It can be used in any place where the value returned depends on the
strategy.
The name used needs to be the same as in
run_model
.
# setting up some initial cost variables
cost_drugA = 2000
cost_drugB = 2700
state_mild= define_state(
# cost of healthcare doesn't depend on strategy (drug)
cost_health = 1500,
# cost of drug depends on strategy
cost_drugs = dispatch_strategy(
drugA = cost_drugA,
drugB = cost_drugB
),
cost_total = cost_health + cost_drugs,
QALYs = 0.9 # QALYs is our choice of effect here.
## We could use a different measure, so long as we linked this to the
## 'effect' argument in run_model.
)
Note that we now specify cost_health
,
cost_drugs
and cost_total
.
Discounting is a general health economic principle, where something’s value in the future is worth less than it is now.
For example here, both costs depreciate by 3% each year, using the
function discount
.
state_severe = define_state(
cost_health = discount(3000, 0.03),
cost_drugs = discount(
dispatch_strategy(
drugA = cost_drugA,
drugB = cost_drugB),
0.03),
cost_total = cost_health + cost_drugs,
QALYs = 0.6
)
state_dead = define_state(
cost_health = 0,
cost_drugs = 0,
cost_total = 0,
QALYs = 0
)
Discounting is often used for QALYs in long-term modelling.
In this model, the different treatment strategies are distinguished by:
We now combine this information together to generate our two Strategies.
strat_drugA = define_strategy(
transition = tm_drugA,
mild = state_mild, # the state definitions distinguish between strategies
severe = state_severe, # via `dispatch_strategy`
dead = state_dead
)
strat_drugB = define_strategy(
transition = tm_drugB,
mild = state_mild,
severe = state_severe,
dead = state_dead
)
We include both strategies in the run_model
function.
## 2 strategies run for 143 cycles.
##
## Initial state counts:
##
## mild = 1000L
## severe = 0L
## dead = 0L
##
## Counting method: 'life-table'.
##
##
##
## Counting method: 'beginning'.
##
##
##
## Counting method: 'end'.
##
## Values:
##
## cost_health cost_drugs cost_total QALYs
## drugA 24645721 19649231 44294952 8669.997
## drugB 31508935 34547463 66056398 12578.177
##
## Efficiency frontier:
##
## drugA -> drugB
##
## Differences:
##
## Cost Diff. Effect Diff. ICER Ref.
## drugB 21761.45 3.908179 5568.18 drugA
counts in each state
Costs and effects over time:
These are all built in ggplot2
, so if you would like to
change the appearance in any way (eg. axis labels, colours) you can use
the usual ggplot2
syntax.
We have seen
heemod
You can now work through the tasks and material in the
workshop.
Sections 1 - 4 go over the same material we
have just covered.