Skip to contents

Getting started

twig is an R package for building decision and cost-effectiveness analyses. Below we provide instructions to install the twig R package, a simple Markov model example, and reference two cost-effectivenss tutorials, one on a time-dependent Markov model and the other on a decision tree. For building twig syntax interactively, you can use DecisionTwig which is an online graphical user interface for developing twig syntax.

Installation

The twig package is now available on CRAN and can be installed directly in R using the following command:

Alternatively, to install the latest development version from GitHub, use:

Overview

twig streamlines the process of building models by defining a Grammar of Modeling inspired by the Grammar of Graphics used in the ggplot2 package. twig leverages vectorized operations for efficiency.

A minimal example:

Consider this twig syntax:

library(twig)
mytwig <- twig() + 
  decisions(names = c(A, B)) +  # Decision alternatives
  states(names = c(Alive, Dead),  # Markov state names
         init_probs = c(1, 0)) +  # The cohort starts healthy
  event(name = death_event,  # A death event can   
        options = c(yes, none),  # have two options: "yes" and "none",
        probs = c(pDie, leftover),  # occur with probabilities: pDie and leftover = 1 - pDie
        transitions=c(Dead,stay)) + # can lead to death state otherwise stay in their current state, respectively.
  payoffs(names = c(cost, utility))  # Payoff function names

The concept of Grammar of Modeling is insipred by ggplot’s Grammar of Graphics. The key benefit of adopting this grammar is to minimize repetition in decision and cost-effectiveness analysis modeling to streamline model building, maintenance and debugging. The twig above consists of a decisions layer that includes the names of the alternative strategies or choices, a states layer that describes the Markov states and their initial probabilities, an event layer die_event, and finally a payoffs layer describing how payoffs are accumulated.

The key component of the Grammar of Modeling is to develop a generic sequence of events (twig) that applies to the entire population. The flow through these events can be controlled by the probability functions probs which can depend on the decision, state, cycle for age dependency, cycle_in_state for tunnels, and , and prior event names (e.g., death_event) in the twig.

DecisionTwig

In DecisionTwig, this twig will look like this

DecisionTwig allows to interactively build the twig syntax. This can be especially helpful for more complex event sequence structure.

Probability and payoff functions

Next, we define the three functions that we used in the twig: pDie, cost and utility. Note that these functions are all vectorized, meaning that they can take a vector of arguments (e.g., states, decisions, and parameters) and return a vector of probabilities, costs, and utilities. This is a key feature of twig that allows for efficient computation of the model across multiple simulations and avoiding for loops.

These functions can take in core twig arguments (decision, state, cycle, cycle_in_state and event names), and simulation variables in the params. Global variables can be used inside the functions without passing them as arguments.

# 1. probability of death depends on the state and decision and the relative risk of mortality given treatment A
pDie <- function(state, decision, rrMortA){
  rDie <- 0.01*(state=="Alive") * rrMortA^(decision=="A") # rate of death is 20% if alive, 0 otherwise. This rate is multiplied by rrMortA for A, otherwise 1.
  rate2prob(rDie) # convert the rate into probability
}

# 2. cost depends on the decision
cost <- function(decision, cA, cB){
  cA * (decision=="A") + # if A, 0 otherwise
  cB * (decision=="B") # if B, 0 otherwise
}

# 3. utility depends on the state = uAlive if alive, otherwise 0
utility <- function(state, uAlive){
  uAlive * (state=="Alive") # only if alive 0 otherwise
}

Here we use a concise way to define if-else statements. For example, cA * (decision=="A") is equivalent to ifelse(decision=="A", cA, 0). Both statements are vectorized and can take a vector of decisions and return a vector of costs. However, the former is more concise and easier to read because it avoids nesting multiple ifelse statements.

We also used rrMortA^(decision=="A") to apply the relative risk of mortality if the decision is A, otherwise 1. This is because decision=="A" is treated as 1, and decision!="A" is treated as 0. By combining * and ^ we can concisely express multiple conditional statements.

Parameters

Then, we can create a probabilistic dataframe of our parameter samples:

n_sims <- 1000 # number of simulations

psa_params <- data.frame(
  rrMortA = rnorm(n_sims, 0.9, 0.1), # Normal: relative risk of mortality for A vs. B
  cA = rlnorm(n_sims, 6, 1), # Log-normal: cost of A
  cB = rlnorm(n_sims, 5, 1), # Log-normal: cost of B
  uAlive = rbeta(n_sims, 0.8, 0.2)) # Beta: utility of being alive

head(psa_params) # examining the first 6 samples

#     rrMortA         cA        cB    uAlive
# 1 1.0168738 1270.91187  34.93591 0.9999919
# 2 0.8952317 1433.50158 351.25445 0.9497743
# 3 0.7524302   90.52039 273.80137 0.9999980
# 4 0.8276866  245.09993 488.33008 0.9805013
# 5 0.8751724   52.21374 219.23235 0.8402547
# 6 0.9473632  396.90634 112.18042 0.9991721

twig also accepts a list of parameter scalar values.

Lastly, we run the model for 50 cycles (years) and compute the average expected values (EV) for the costs and utilities of both strategies.

results <- run_twig(twig_obj = mytwig, params = psa_params, n_cycles = 50)

results$mean_ev #average across all simulations
#         payoff
# decision     cost  utility
#        A 32379.32 32.11033
#        B 12503.32 31.32062

Note that your results may be slightly different due to the randomness in params.

Incremental Cost-Effectiveness Ratio (ICER):

We can produce the incremental cost-effectiveness ratio (ICER) by adapting dampack’s calculate_icer function:

# calculate_icers(results$mean_ev)
#   decision     cost  utility inc_cost inc_utility     ICER status
# B        B 12503.32 31.32062       NA          NA       NA     ND
# A        A 32379.32 32.11033 19875.99   0.7897148 25168.57     ND

ND = Not dominated

Cost-Effectiveness Acceptability Curve (CEAC):

We can also plot the cost-effectiveness acceptability curve (CEAC) using a range of willingness to pay (WTP) thresholds using the plot_ceac function:

plot_ceac(results$sim_ev, wtp_range = seq(0, 100000, by = 1000))

This brief tutorial demonstrated the basic functionality of the twig package with a simple Markov model. It shows how to define a basic twig, define the probabilistic input data, run the model, create the ICER table, and produce the CEAC curves. To illustrate more advanced functionality of twig, we provide two vignettes, one on a time-dependent Markov model and the other on a decision tree.

Additional Resources:

  1. Time-dependent Markov model using the sick-sicker model which illustrates the following features:
  • transition probabilities dependent on decisions and states
  • transition probabilities dependent on simulation time (i.e., Markov cycle)
  • transition probabilities dependent on state residency (i.e., cycle in Markov state) or tunnels
  • transition probabilities dependent on prior events in a sequence of events within each cycle
  • payoffs dependent on decisions and states
  • payoffs dependent on events
  • payoff discounting
  • parallel computation
  1. Decision-tree illustrating the cost-effectiveness of herpes simplex encephalopathy
  • building a decision tree
  • multiple sequential events
  • event probabilities dependent on decisions
  • event probabilities depending on prior events in a sequence of events
  • define final outcomes in a decision tree
  • payoff dependent on decisions
  • payoff dependency on events and final outcomes

Disclaimer

Please note that both DecisionTwig and twig are still under active development and are provided as-is without any warranty.

License

This project is licensed under the GPL v3 International.

Suggested citations:

Jalal, H. (2024). Grammar of Modelling, twig R package. Retrieved from https://github.com/hjalal/twig

Jalal, H. (2024). DecisionTwig. Retrieved from https://www.dashlab.ca/projects/decision_twig/