Data & Functions
Competing Risks Curves



Data


We load the packages we will use :

# libraries
library(cmprsk)
library(survminer)
library(ggplot2)

We create a database :

set.seed(2)
df <- data.frame(del=rexp(100)*5, event=sample(0:2,100,replace=TRUE), groupe=sample(c("A", "B"),100,replace=TRUE))
df$event_surv <- ifelse(df$event==0, 0, 1)



#Head of dataset
knitr::kable(head(df,8), align = "l")
del event groupe event_surv
9.3267622 1 B 1
2.0237404 1 B 1
0.7332633 0 B 0
8.6535486 1 B 1
0.4476309 2 B 1
3.3344882 0 B 0
5.3718343 0 B 0
7.5581465 2 B 1

We create the differents survfit objects we will use :

fit <- cuminc(df$del, df$event)
fit_surv <- survfit(Surv(del, event_surv)~1, data=df)

fit_gp <- cuminc(df$del, df$event, df$groupe)
fit_surv_gp <- survfit(Surv(del, event_surv)~groupe, data=df)

ggcompetingrisks function


ggcompetingrisks(
fit, an object of a class cmprsk::cuminc - created with cmprsk::cuminc function or survfitms created with survfit function
gnames, a vector with group names. If not supplied then will be extracted from fit object
gsep, a separator that extracts group names and event names from gnames object
multiple_panels, if TRUE then groups will be plotted in different panels
ggtheme, ggplot2 theme name. Default value is theme_survminer (see ggplot2 page)
coef, see conf.int, scaling actor for the ribbon. The default value is 1.96
conf.int, if TRUE then additional layer (geom_ribbon) is added around the point estimate. The ribon is plotted with boundries +- coef*standard deviation
... )

ggrisktable function


ggrisktable(
fit, an object of class survfit
data, a dataset used to fit survival curves
risk.table.type, risk table type. Allowed values : c(“absolute”, “percentage”, “abs_pct”, “nrisk_cumcensor”, “nrisk_cumevents”)
survtable, a character string specifying the type of survival table to plot
title, the title of the plot
risk.table.title, the title to be used for the risk table
break.time.by, numeric value controlling time axis breaks. Default value is NULL.
y.text, logical. Default is TRUE. If FALSE, the table y axis tick labels will be hidden
y.text.col, logical. Default value is FALSE. If TRUE, the table tick labels will be colored by strata
fontsize, text font size
tables.theme, function, ggplot2 theme name (see ggplot2 page)
color, color to be used for the survival curves
legend, legend position. Allowed values are one of c(“top”, “bottom”, “left”, “right”, “none”)
... )

ggcompetingrisks1 function


This new function allows you to not display the events you don’t want to plot with the option event_suppr.
It also allows an improvement of the visual if only one group.
(See here to display only the cumulative incidence of interest and here to display in 2 groups)

# Cumulative Incidence Curves for Competing Risks function

ggcompetingrisks1 <- function(fit, gnames = NULL, gsep=" ", event_suppr=NULL, 
                              multiple_panels = FALSE,
                              ggtheme = theme_survminer(),
                              coef = 1.96, conf.int = FALSE, ...) {
  # stopifnot(any(class(fit) %in% c("cuminc", "survfitms")))
  
  if (any(class(fit) == "cuminc")) {
    pl <- ggcompetingrisks.cuminc1(fit = fit, gnames=gnames, 
                                   gsep=gsep, multiple_panels=multiple_panels,
                                   coef = coef, conf.int = conf.int, event_suppr=event_suppr)
  }
  
  if (any(class(fit) == "list") & any(class(fit[[1]]) == "cuminc")) {
    pl <- ggcompetingrisks.cuminc.combine(fit = fit, gnames=gnames, 
                                   gsep=gsep, multiple_panels=multiple_panels,
                                   coef = coef, conf.int = conf.int, event_suppr=event_suppr)
  }
  
  if (any(class(fit) == "survfitms")) {
    pl <- ggcompetingrisks.survfitms(fit = fit)
  }
  
  pl <- pl + ggtheme +
    ylab("Probability of an event") + xlab("Time") +
    ggtitle("Cumulative incidence")
  ggpubr::ggpar(pl, ...)
}


ggcompetingrisks.cuminc1 <- function(fit, gnames = NULL, gsep=" ", 
                                     multiple_panels = FALSE, coef = 1.96, conf.int = FALSE, event_suppr=NULL) {
  if (!is.null(fit$Tests))
    fit <- fit[names(fit) != "Tests"]
  fit2 <- lapply(fit, `[`, 1:3)
  if (is.null(gnames)) gnames <- names(fit2)
  fit2_list <- lapply(seq_along(gnames), function(ind) {
    df <- as.data.frame(fit2[[ind]])
    df$name <- gnames[ind]
    df
  })
  time <- est <- event <- group <- NULL
  df <- do.call(rbind, fit2_list)
  df$event <- sapply(strsplit(df$name, split=gsep), `[`, 2)
  df$group <- sapply(strsplit(df$name, split=gsep), `[`, 1)
  df$std <- std <- sqrt(df$var)
  
  if(!is.null(event_suppr)){
    df=df[!df$event %in% event_suppr, ]
    if(unique(df$event)==1) {df$event=NULL}
  }
  
  if(length(unique(df$group))==1){
    pl <- ggplot(df, aes(time, est, color=event), legend="none")
  } else {
    if (multiple_panels) {
      pl <- ggplot(df, aes(time, est, color=event)) + facet_wrap(~group)
    } else {
      pl <- ggplot(df, aes(time, est, color=group, linetype=event))
    }
  }
  
  
  if (conf.int) {
    pl <- pl + geom_ribbon(aes(ymin = est - coef*std, ymax=est + coef*std), alpha = 0.2, linetype=0)
  }
  
pl + geom_line()

}



ggcompetingrisks.cuminc.combine <- function(fit, gnames = NULL, gsep=" ", 
                                     multiple_panels = TRUE, coef = 1.96, conf.int = FALSE, event_suppr=NULL) {
  # if (!is.null(fit$Tests))
  #   fit <- fit[names(fit) != "Tests"]
  
  fit2=fit
  for(i in 1:length(fit)){
    fit2[[i]]=lapply(fit2[[i]], `[`, 1:3)
  }
  
  fit2_list=fit2
  
  fit2_list=lapply(1:length(fit2_list), function(i) {
    gnames <- names(fit2_list[[i]])
    fit2_list[[i]] <- lapply(seq_along(gnames), function(ind) {
      df <- as.data.frame(fit2[[i]][[ind]])
      df$name <- gnames[ind]
      df$event <- sapply(strsplit(df$name, split=gsep), `[`, 2)
      df$group <- i
      df
    })
    fit2_list[[i]]=do.call(rbind, fit2_list[[i]])
  })
  
  time <- est <- event <- group <- NULL
  df <- do.call(rbind, fit2_list)
  
  
  df$std <- std <- sqrt(df$var)
  
  df$group=factor(df$group)
  
  if(!is.null(event_suppr)){
    df=df[!df$event %in% event_suppr, ]
    pl <- ggplot(df, aes(time, est, color=group)) 
  }
  
  if(is.null(event_suppr)){
    pl <- ggplot(df, aes(time, est, color=group, linetype=event)) 
  }
  
  # if (multiple_panels) {
  #   pl <- ggplot(df, aes(time, est, color=event + facet_wrap(~group)))
  # } else {
  #   pl <- ggplot(df, aes(time, est, color=group, linetype=event))
  # }
  if (conf.int) {
    pl <- pl + geom_ribbon(aes(ymin = est - coef*std, ymax=est + coef*std), alpha = 0.2, linetype=0)
  }
  
  pl + geom_line()
  
}
ggcompetingrisks.survfitms <- function(fit) {
  times <- fit$time
  psta <- as.data.frame(fit$pstate)
  colnames(psta) <- fit$states
  if (is.null(fit$strata)) {
    psta$strata <- "all"
  } else {
    psta$strata <- rep(names(fit$strata), fit$strata)
  }
  psta$times <- times
  psta <- .rename_empty_colname(
    psta, newname = "."
  )
  
  event <- value <- strata <- NULL
  pstal <- gather(psta, event, value, -strata, -times)
  ggplot(pstal, aes(times, value, fill=event)) +
    geom_area() + facet_wrap(~strata)
  
}

.rename_empty_colname <- function(df, newname = "."){
  empty.col <- colnames(df) == ""
  empty.col.exist <- length(empty.col) > 0
  if(empty.col.exist)
    colnames(df)[empty.col] <- newname
  df
}



Contact

This document is a work by Emma Lafaurie (emma.lafaurie@inserm.fr) for the SBIM (Service de Biostatistique et Information Médicale) at Saint-Louis Hospital in Paris.
Based on the template of Yan Holtz.