--- title: "Multi-State Models for Oncology" author: "Kevin Kunzmann" bibliography: references.bib output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{oncomsm} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r knitr-options, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 7 / 1.61, fig.align = "center" ) ``` ```{r setup} library(oncomsm) library(dplyr, warn.conflicts = FALSE) library(ggplot2) ``` **tl;dr:** *The multi-state characteristics of RECIST-like visit data in oncology can be exploited to reduce bias at interim analyses of objective response rate and drive event prediction for probability of success/go calculations.* In early oncology trials, (objective tumor) response based on the RECIST criteria is often used as primary endpoint to establish the activity of a treatment. Often, response is treated as binary variable although it is a delayed event endpoint. At the final analysis, this simplification is of little concern since all individuals tend to be followed up long enough to ignore the small amount of censoring. However, when continuously monitoring such a trial, the assumption of sufficient follow-up is no longer fulfilled at interim analyses and a simple binary analysis is biased. The problem can be addressed by extending the statistic binary response model to a three-state model for "stable", "response", and "progression or death". The respective transition numbers are given in the graph below. ```{r msm-1, eval=TRUE, echo=FALSE} DiagrammeR::mermaid(" graph LR stable -- 1 --> response response -- 3 --> progression[progression or death] stable -- 2 --> progression[progression or death] ", height = 200) ``` Often, a hazard-based approach is used to model multi-state data. However, a hazard-based approach has several disadvantages: 1. The hazard scale is difficult to interpret since it is a momentary risk, not a probability. This leads to problems with prior specification in a Bayesian setting. The Bayesian approach is, however, particularly useful in the early development process since it allows to augment data with prior opinion or evidence and thus improve accuracy. 2. A hazard based, non-Markov multi-state model leads to intractable expressions for the implicitly given transition probabilities. Hence they need to be calculated by simulation which makes the model less convenient to work with if transition probabilities a or of primary interest. Since the (objective) response rate often plays an important role in the analysis of early oncology trials, this is a disadvantage. An alternative framework to model multi-state data are mixture models. For details, see @jackson_comparison_2022 and @jackson_flexible_2022. Here, we describe the concrete application to he simplified "stable", "response", "progression" model. The approach is similar to @aubel_bayesian_2021 and @beyer_multistate_2020. Here, a semi-Markov approach is used. This means that the time to the next transition only depends on the time already spent in a state, not on the full history of previous jumps. Additionally, it is assumed that the transition times between states conditional on both originating and target state can be described by Weibull distributions. This parametric family encompasses the exponential distribution with constant transition rates as special cases but also allows increasing or decreasing hazards over time. Let $T_{S}$ be the transition time from the "stable" state and $T_{R}$ the transition time from the "response" state (also "sojourn" times). Let further $R$ be a binary random variable with $R=1$ if a response occurs, then the model can be specified as: $$ \begin{align} R &\sim \operatorname{Bernoulli}(p) \\ T_{S} \,|\, R = 1 &\sim \operatorname{Weibull}(k_1, \lambda_1) \\ T_{S} \,|\, R = 0 &\sim \operatorname{Weibull}(k_2, \lambda_2) \\ T_{R} &\sim \operatorname{Weibull}(k_3, \lambda_3) \end{align} $$ where $k$ is the vector of shape- and $\lambda$ the vector of scale parameters. Let further $f_i(t)$ be the PDF of the Weibull distribution of transition $i\in\{1,2,3\}$ as indicated in the above figure and let $F_i(t)$ the corresponding CDF. This model implies that $$ \operatorname{Pr}\big[\,T_{S} > t \,\big] = p\cdot(1 - F_1(t)) + (1 - p)\cdot(1 - F_2(t))\\ $$ hence, it can be seen as a mixture model. The median of a Weibull-distributed random variable is directly related to shape and scale parameters. Since the former is more convenient to interpret, the Weibull distributions are parameterized directly via their shape and median. The scale parameter can then be recovered via the relationship $$ \operatorname{scale} = \frac{\operatorname{median}}{\log(2)^{1/\operatorname{shape}}} . $$ The following prior classes are used. For the response probability $p$, a $(1-\eta)\,\operatorname{Beta}(a,b)+\eta\,\operatorname{Unif}(0,1)$ is used. The prior is specified via the equivalent sample size $a+b$ and the mean of the informative component $a/(a+b)$. A log-normal distribution is used for the median time-to-next event, the parameters are inferred from specified $0.05$ and $0.95$ quantiles. A log-normal distribution is used for the shape parameter of the Weibull distributions, the parameters are inferred from specified $0.05$ and $0.95$ quantiles. It is assumed that the observation process (visit spacing) is fixed. Since transitions can only be recorded after the next visit, all transition times are interval censored. Recruitment times are assumed to follow a poisson distribution. Default parameters assume a timescale in months. ## Specifying the model The following code defined the prior assumptions for a two-group trial with a visit-spacing of 1 months. The prior variance on the shape parameter is very low. For sampling from the prior, this does not not matter and more uncertainty about the Weibull shape parameters can be assumed. During posterior inference, identifying shape and scale (median time to event) from a small sample of interval censored observations is not feasible and leads to divergent MCMC samples. The shape thus has to be kept almost fixed in most cases when doing inference. ```{r specify-model} mdl <- create_srpmodel( A = define_srp_prior( p_mean = 0.4, p_n = 10, median_t_q05 = c(3, 2, 6) - 1, # s->r s->p r->p median_t_q95 = c(3, 2, 6) + 1 ), B = define_srp_prior( p_mean = 0.6, p_n = 10, median_t_q05 = c(2, 8, 3) - 1, # s->r s->p r->p median_t_q95 = c(2, 8, 12) + 1, shape_q05 = c(2, 2, 0.75), shape_q95 = c(2.1, 2.1, 0.76) ) ) print(mdl) ``` The model assumptions can be visualized by sampling from the prior. ## Prior checks First, we plot the cumulative distribution functions (CDF) of the time-to-next-event over the first 36 (months) and the CDF of the response probabilities per group. These are based on a sample drawn from the prior distribution of the model. We can re-use the same parameter sample for sampling from the prior-predictive distribution by separating the sampling from the plotting steps. Often, the rate of progression free survival (PFS) at a particular time point is of interest. This quantity is a direct function of the model parameters. Since the simplified model does not distinguish between progression or death, we denote the combined endpoint as "progression". $$ \begin{align} \operatorname{PFS}(t) :&= \operatorname{Pr}\big[\,\text{no progression before } t\,\big] \\ &= 1 - \operatorname{Pr}\big[\,\text{progression before } t\,] \\ &= 1 - \Big(\ \operatorname{Pr}\big[\,\text{progression before } t\,|\, \text{response}\,]\cdot\operatorname{Pr}\big[\,\text{response}\,] \\ &\qquad+ \operatorname{Pr}\big[\,\text{progression before } t\,|\, \text{no response}\,]\cdot\operatorname{Pr}\big[\,\text{no response}\,] \ \Big)\\ &= 1 - p\cdot\int_0^t f_1(u) \cdot F_3(t - u) \operatorname{d}u - (1 - p)\cdot F_2(t) \ . \end{align} $$ The integral arises from the need to reflect the uncertainty over the state change from "stable" to "response" on the way to "progression". Any parameter sample thus also induces a sample of the PFS rate at any given time point and the curve of PFS rate over time corresponds to the survival function of the "progression or death" event. ```{r plotting-the-prior} smpl_prior <- sample_prior(mdl, seed = 36L) # plot(mdl) also works but need to resample prior further below plot(mdl, parameter_sample = smpl_prior, confidence = 0.75) ``` ## Sampling from the prior-predictive distribution Next, we draw samples from the prior-predictive distribution of the model. We sample 100 trials with 30 individuals per arm. Here, we can re-use the sample prior sample already used for plotting. ```{r prior-predictive} tbl_prior_predictive <- sample_predictive( mdl, sample = smpl_prior, n_per_group = c(30L, 30L), nsim = 100, seed = 342 ) print(tbl_prior_predictive, n = 25) ``` We can then run some quick checks on the sampled data, e.g., the observed response rates. ```{r} tbl_prior_predictive %>% group_by(group_id, iter, subject_id) %>% summarize( responder = any(state == "response"), .groups = "drop" ) %>% group_by(group_id) %>% summarize( p_response = mean(responder), se = sd(responder) / sqrt(n()) ) ``` A crude approximation of the median transition times can be compared with the prior means. ```{r} tbl_prior_predictive %>% distinct(subject_id, iter, state, .keep_all = TRUE) %>% group_by(iter, group_id, subject_id) %>% summarize( dt = t - lag(t), from = lag(state), to = state, .groups = "drop" ) %>% filter(to != "stable") %>% group_by(group_id, from, to) %>% summarize( `median transition time` = median(dt), .groups = "drop" ) ``` By default, the prior predictive distribution is given in terms of panel visit data. The data can be transformed to interval-censored multi-state representation, (here only first sampled trial). ```{r convert-to-mstate} tbl_mstate <- tbl_prior_predictive %>% filter(iter == 1) %>% visits_to_mstate(mdl) tbl_mstate ``` The multi-state data can be visualized in swimmer plots. ```{r plot-preior-predictive, fig.height=6} plot_mstate(tbl_mstate, mdl) ``` It is also possible to simulate from the prior predictive distribution while fixing some of the parameter values. Fixing parameter values can be interpreted as conditioning on some or all of the parameters. For instance one could set the response probabilities to fixed values of $0.1$ and $0.9$: ```{r prior-predictive-fixed} sample_predictive( mdl, sample = smpl_prior, p = c(0.1, 0.9), n_per_group = c(30L, 30L), nsim = 100, seed = 3423423 ) %>% group_by(group_id, iter, subject_id) %>% summarize( responder = any(state == "response"), .groups = "drop" ) %>% group_by(group_id) %>% summarize( p_response = mean(responder), se = sd(responder) / sqrt(n()) ) ``` ## A hypothetical interim analysis First, we sample a single data set under extreme response probabilities that deviate from the chosen prior. The data can then be curtailed to a hypothetical interim time-point simply by filtering the visit time-points. ```{r} tbl_data_interim <- sample_predictive( mdl, sample = smpl_prior, p = c(0.2, 0.8), n_per_group = c(30L, 30L), nsim = 1, seed = 42L ) %>% filter( t <= 15 ) ``` The censoring in the interim data can be visualized in a swimmer plot again. ```{r, fig.height=6} tbl_data_interim %>% visits_to_mstate(mdl, now = 15) %>% plot_mstate(mdl, relative_to_sot = FALSE, now = 15) ``` We can check the observed response rates again. Due to censoring at the interim time point, the response rate estimate is biased. ```{r} tbl_data_interim %>% group_by(group_id, iter, subject_id) %>% summarize( responder = any(state == "response"), .groups = "drop" ) %>% group_by(group_id) %>% summarize( p_response = mean(responder), se = sd(responder) / sqrt(n()) ) ``` Instead, one can now do inference by drawing sample from the posterior distribution this will account for censoring. Since the data conflicts with the prior, the posterior mass will move in the direction of the observed response rates. ```{r} smpl_posterior <- sample_posterior(mdl, tbl_data_interim, seed = 43L) # plot under posterior plot(mdl, parameter_sample = smpl_posterior, confidence = 0.75) # calculate posterior quantiles of response probability smpl_posterior %>% parameter_sample_to_tibble(mdl, .) %>% filter(parameter == "p") %>% group_by(group_id) %>% summarize( p_posterior_mean = median(value), q25 = quantile(value, probs = .25), q75 = quantile(value, probs = .75) ) ``` Alternatively, the analysis could also be run using the default weakly-informative prior. For details on the prior choice, see the corresponding vignette. ```{r} mdl2 <- create_srpmodel( A = define_srp_prior(), B = define_srp_prior() ) smpl_posterior2 <- sample_posterior(mdl2, tbl_data_interim, seed = 43L) # plot under posterior plot(mdl2, parameter_sample = smpl_posterior2, confidence = 0.75) # calculate posterior quantiles of response probability smpl_posterior2 %>% parameter_sample_to_tibble(mdl2, .) %>% filter(parameter == "p") %>% group_by(group_id) %>% summarize( p_posterior_mean = median(value), q25 = quantile(value, probs = .25), q75 = quantile(value, probs = .75) ) ``` ## Session info ```{r session-info} sessionInfo() ```