This is a companion discussion topic for the original entry at https://rviews.rstudio.com/2020/11/02/simulating-biologically-plausible-survival-data

In two recent posts, one on the Disease Progression Model and the other on Fake Data, I highlighted some of `R's`

tools for simulating data that exhibit desired correlations and other statistical properties. In this post, I’ll focus on a small cluster of `R`

packages that support generating biologically plausible survival data.

### Background

In an impressive paper *Simulating biologically plausible complex survival data* Crowther & Lambert (2013) that combines survival analysis theory and numerical methods, Michael Crowther and Paul Lambert address the problem of simulating plausible data in which event time, censuring and covariate distributions are plausible. They develop a methodology for conducting survival analysis studies, and also provide computational tools for moving beyond the usual exponential, Weibull and Gompertz models. Building on the work by Bender et al. (2005) in establishing a framework for simulating survival data for Cox proportional hazards models, Crowther and Lambert discuss how modelers can incorporate non proportional model hazards, time varying effects, delayed entry and random effects and provide code examples based on the `Stata`

`survsim`

package.

### The `survsim`

package

Not longer after the `Stata`

package appeared, Moriña and Navarro released the `R`

`survsim`

package which implements some of the features in the `Stata`

package for simulating complex survival data. The `R`

package does not have a vignette, but you can find several examples in the *JSS* paper Moriña & Navarro (2014).

The following example from section 4.3 of the paper simulates adverse events for a clinical trial with 100 patients followed up for 30 days. The authors suggest that the three covariates **x** could represent body mass index, age at entry to the cohort, and whether or not the subject has hypertension. This is a little bit unusual and sophisticated example of survival modeling.

```
set.seed(12345)
dist.ev <- c("weibull", "llogistic", "weibull")
anc.ev <- c(0.8, 0.9, 0.82)
beta0.ev <- c(3.56, 5.94, 5.78)
beta <- list(c(-0.04, -0.02, -0.01), c(-0.001, -0.0008, -0.0005),c(-0.7, -0.2, -0.1))
x <- list(c("normal", 26, 4.5), c("unif", 50, 75), c("bern", 0.25))
clinical.data <- mult.ev.sim(n = 100, # number of patients in cohort
foltime = 30, # maximal followup time
dist.ev, # time to event distributions (t.e.d.)
anc.ev, # parameters for t.d.e. distributions
beta0.ev, # beta0 parameters for t.d.e. dist
dist.cens = "weibull", #censoring distribution
anc.cens = 1, # parameters for censoring dist
beta0.cens = 5.2, # beta0 for censoring dist
z = list(c("unif", 0.6, 1.4)), # random effect dist
beta, # effect of covariate
x, # distributions of covariates
nsit = 3) # max number of adverse events for an individual
head(round(clinical.data,2))
```

```
## nid ev.num time status start stop z x x.1 x.2
## 1 1 1 5.79 1 0 5.79 0.97 28.63 69.02 1
## 2 1 2 30.00 0 0 30.00 0.97 28.63 69.02 1
## 3 1 3 30.00 0 0 30.00 0.97 28.63 69.02 1
## 4 2 1 3.37 1 0 3.37 0.60 36.42 53.81 0
## 5 2 2 30.00 0 0 30.00 0.60 36.42 53.81 0
## 6 2 3 30.00 0 0 30.00 0.60 36.42 53.81 0
```

### The `simsurv`

package

In the vignette on *How to use the `simsurv`

package, the package authors Sam Brilleman and Alessandro Gasparini state that they directly modeled their package on the `Stata`

package `survsim`

and cite the Crowther and Lambert paper. They show how `survsim`

builds out much of the functionality envisioned there in examples that demonstrate the interplay between model fitting and simulation. Example 2 of the vignette is concerned with constructing fake data modeled on the German breast cancer data by Schumacher et al. (1994).

```
data("brcancer")
head(brcancer)
```

```
## id hormon rectime censrec
## 1 1 0 1814 1
## 2 2 1 2018 1
## 3 3 1 712 1
## 4 4 1 1807 1
## 5 5 0 772 1
## 6 6 0 448 1
```

The example begins by fitting alternative models to the data using functions from the `flexsurv`

package of Jackson, Metcalfe and Amdahl. Two candidate models are proposed and a spline model giving the best fit is used to simulate data. The example concludes with more model fitting to examine the fake data. All of the examples in the vignette showcase the interplay between `simsurv`

and `flexsurv`

functions and emphasize the flexible modeling tools in `flexsruv`

for building custom survival models.

The following code replicates the portion of Example 2 that illustrates the use of the `flexsurvspline()`

function which allows the calculation of the log cumulative hazard function to depend on knot locations.

The code below produces the simulated data and uses the `survminer`

package of Kassambara et al. to produce high quality Kaplan-Meier plots.

This line of code fits a three knot spline model to the `brcancer`

data. The `flexsurvspline()`

function, as with other functions in the `flexsurv`

package build on the basic functionality of the fundamental Terry Therneau’s `survival`

package.

```
true_mod <- flexsurv::flexsurvspline(Surv(rectime, censrec) ~ hormon,
data = brcancer, k = 3)
```

This helper function returns the log cumulative hazard at time t

```
logcumhaz <- function(t, x, betas, knots) {
# Obtain the basis terms for the spline-based log
# cumulative hazard (evaluated at time t)
basis <- flexsurv::basis(knots, log(t))
# Evaluate the log cumulative hazard under the
# Royston and Parmar specification
res <-
betas[["gamma0"]] * basis[[1]] +
betas[["gamma1"]] * basis[[2]] +
betas[["gamma2"]] * basis[[3]] +
betas[["gamma3"]] * basis[[4]] +
betas[["gamma4"]] * basis[[5]] +
betas[["hormon"]] * x[["hormon"]]
res
}
```

The `simsurv()`

functions generates the simulated survival data.

```
covariates <- data.frame(id = 1:686, hormon = rbinom(686, 1, 0.5))
sim_data <- simsurv(
betas = true_mod$coefficients, # "true" parameter values
x = covariates, # covariate data for 686 individuals
knots = true_mod$knots, # knot locations for splines
logcumhazard = logcumhaz, # definition of log cum hazard
maxt = NULL, # no right-censoring
interval = c(1E-8,100000)) # interval for root finding
sim_data <- merge(covariates, sim_data)
head(sim_data)
```

```
## id hormon eventtime status
## 1 1 1 240.4 1
## 2 2 0 942.6 1
## 3 3 1 463.5 1
## 4 4 0 1762.0 1
## 5 5 0 3976.0 1
## 6 6 0 2288.0 1
```

We use the `surv_fit`

function from the `survminer`

package to fit the Kaplan-Meier curves

```
KM_data <- survminer::surv_fit(Surv(rectime, censrec) ~ 1, data = brcancer)
KM_data_sim <- survminer::surv_fit(Surv(eventtime, status) ~ 1, data = sim_data)
```

Finally, plotting the curves shows that the simulsted data does appear to plausibly resemble the original data.

```
p <- ggsurvplot_combine(list(KM_data, KM_data_sim),
risk.table = TRUE,
conf.int = TRUE,
censor = FALSE,
conf.int.style = "step",
tables.theme = theme_cleantable(),
palette = "jco")
plot.new()
print(p,newpage = FALSE)
```

I hope you find this small post helpful. The CRAN task view on Survival Analysis is a fantastic resource, but it can be a daunting task for non-experts to know where to begin to unravel the secrets there without a thread to pull on.