knitr::opts_chunk$set( collapse = TRUE, comment = "#>", echo = TRUE, eval = TRUE, fig.height = 6, fig.width = 9, fig.align='center' )
After inserting the survfit{survival}
object into surv.plot{survSAKK}
, we can create simple survival curves, allowing to visualize survival patterns and incorporate various statistics in our plot.
To show some benefit of this function, NCCTG Lung Cancer Data, available in the survival package is used.
# Load required libraries library(survSAKK) library(survival) # Load lung data lung <- survival::lung # Compute survival time in months and years lung$time.m <- lung$time/365.25*12 lung$time.y <- lung$time/365.25 # Create survival objects fit.lung.d <- survfit(Surv(time, status) ~ 1, data = lung) fit.lung.m <- survfit(Surv(time.m, status) ~ 1, data = lung) fit.lung.arm.m <- survfit(Surv(time.m, status) ~ sex, data = lung) fit.lung.arm.y <- survfit(Surv(time.y, status) ~ sex, data = lung)
# Single arm surv.plot(fit.lung.m) # Two arm surv.plot(fit.lung.arm.m)
surv.plot(fit.lung.arm.m, # Colour col = c("cadetblue2", "cadetblue"), # Title main = "Kaplan-Meier plot", # Axis label xlab = "Time since treatment start (months)", ylab = "Overall survival (probability)" )
# Choose legend position and names of the arms surv.plot(fit.lung.arm.m, legend.position = "bottomleft", legend.name = c("Male", "Female") ) # Choose legend position manually and add a legend title surv.plot(fit.lung.arm.m, legend.position = c(18, 0.9), legend.name = c("Male", "Female"), legend.title = "Sex" )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), xticks = seq(0, 36, by = 12), yticks = seq(0, 1, by = 0.2) ) # Cut curve at 24 months surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), xticks = seq(0, 24, by = 6) )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), # Global adjustment cex = 1.3, risktable.name.position = -6, risktable.title.position = -6 )
surv.plot(fit.lung.arm.m, main = "Kaplan-Meier plot", legend.name = c("Male", "Female"), legend.title = "Sex", # Size of x-axis label xlab.cex = 1.2, # Size of y-axis label ylab.cex = 1.2, # Size of axis elements axis.cex = 0.8, # Size of the censoirng marks censoring.cex = 1, # Size of the legend title legend.title.cex = 1.2, # Size of the risktable risktable.cex = 0.7, # Size of the risktable name risktable.name.cex = 0.9 )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), xlab.pos = 6, ylab.pos = 5 )
# Change the margins and shift the y axis label surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), # New margin area margin.bottom = 6, margin.left = 7, margin.top = 1, margin.right = 2, # Define margin of the y-axis label ylab.pos = 4 )
The parameter time.unit
can be set as follows: "day"
, "week"
, "month"
,"year"
.
Note the following:
The time unit in time.unit
needs to correspond to the time unit which was used to calculate the survival object fit
.
If time.unit = "month"
x ticks are automatically chosen by intervals of 6 months. Whereas for time.unit = "year"
the x ticks are chosen by intervals of 1.
# Time unit of month surv.plot(fit.lung.arm.m, time.unit = "month", y.unit = "probability", legend.name = c("Male", "Female") ) # Time unit of year surv.plot(fit.lung.arm.y, time.unit = "year", y.unit = "percent", legend.name = c("Male", "Female") )
Per default the risk table is provided below the Kaplan-Meier plot. It provides information about the number of patients at risk at different time points.
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), risktable = FALSE )
# Move risk table names and titles to the left surv.plot(fit.lung.arm.m, legend.name = c("male", "female"), risktable.name.position = -6, risktable.title.position = -6 )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), risktable.name = c("M", "F"), risktable.col = TRUE, risktable.title = "Number at risk", risktable.title.font = 4, risktable.title.col = "#E41A1C" )
surv.plot(fit.lung.arm.m, risktable.censoring = TRUE)
This section explains how to highlight a specific quantile or time point as a segment in the survival curve and how to adjust segment annotation.
# Drawing a segment line for the median, which corresponds to 0.5 quantile surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.quantile = 0.5 ) surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.quantile = 0.5, # Specifying time unit time.unit = "month" )
# Drawing segment for the 0.75 quantile surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.quantile = 0.75 )
# Drawing a segment line at 12 months surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), time.unit = "month", segment.timepoint = 12 )
The parameter segment.annotation
can take the following values: c(x,y)
,"bottomleft"
, "left"
, "right"
, "top"
, "none"
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.timepoint = 18, segment.annotation = "top", time.unit = "month" )
surv.plot(fit.lung.arm.m, col = c("cadetblue2", "cadetblue"), legend.name = c("Male", "Female"), time.unit = "month", segment.quantile = 0.5, segment.font = 10, segment.main.font = 11, segment.main = "Median PFS in months (95% CI)", segment.cex = 0.8, segment.annotation.col = "darkgray" )
Note that segment.annotation
needs to be set to "none". Otherwise the code does not work.
# Several quantiles surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.quantile = c(0.5, 0.25), segment.annotation = "none", time.unit = "month" ) # Several time points surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.timepoint = c(6, 18), segment.annotation = "none", time.unit = "month" )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), time.unit = "month", segment.quantile = 0.5, segment.lwd = 2, segment.lty = "dashed", segment.annotation.space = 0.1 )
Note that the option segment.confint = FALSE
only works for two arms.
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), time.unit = "month", segment.quantile = 0.5, segment.confint = FALSE ) surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), time.unit = "month", segment.timepoint = 18, segment.confint = FALSE, segment.annotation = "bottomleft" )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), segment.quantile = 0.5, conf.int = 0.8 ) surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), time.unit = "month", segment.timepoint = 18, y.unit = "percent", conf.int = 0.9 )
There are three options for the parameter stat
to display statistics:
logrank
: gives the p value of the log rank test calculated using survdiff{survival}
.
coxph
: gives the hazard ratio (HR) and its 95% CI of the conducted Cox proportional hazards regression using coxph{survival}
.
coxph_logrank
: is a combination of logrank
and coxph
.
logrank test
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), stat = "logrank", )
coxph
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), stat = "coxph" )
coxph_logrank
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), stat = "coxph_logrank" )
surv.plot(fit.lung.arm.m, legend.name = c("Male", "Female"), stat = "logrank", stat.position = "right", stat.col = "darkgrey", stat.cex = 0.8, stat.font = 3 )
surv.plot(fit.lung.arm.m, legend.name = c("Female","Male"), stat = "coxph_logrank", reference.arm = 2, stat.conf.int = 0.80 )
In the next example the ECOG performance status is used as stratification factor for the calculation of the statistics.
# Fit survival object with stratification fit_lung_stratified <- survfit(Surv(time.m, status) ~ sex + strata(ph.ecog), data = lung) surv.plot(fit.lung.arm.m, stat.fit = fit_lung_stratified, legend.name = c("Male", "Female"), stat = "coxph_logrank" )
The following themes are implemented: SAKK
, Lancet
, JCO
, WCLC
, ESMO
surv.plot(fit.lung.arm.m, theme = "ESMO")
surv.plot(fit.lung.arm.m, theme = "Lancet")
We present to ways how to combine plots: via par(mfrow=c())
and via split.screen()
par(mfrow=c())
# Save old settings users_default_setting <- par(no.readonly = TRUE)
# Creating 4 sub-plot par(mfrow=c(2,2)) # Plot 1 surv.plot(fit.lung.d) # Plot 2 surv.plot(fit.lung.arm.m, time.unit = "month") # Plot 3 surv.plot(fit.lung.arm.y, col = c("cadetblue2", "cadetblue"), time.unit = "year", stat = "coxph") # Plot 4 surv.plot(fit.lung.arm.m, # Cusomization of the survival plot main = "Kaplan-Meier plot", legend.name = c("Male", "Female"), legend.title = "Sex", xlab.cex = 1.2, ylab.cex = 1.2, axis.cex = 0.8, censoring.cex = 1, legend.title.cex = 1.2, # Customization of the risktable risktable.name.position = -9, risktable.title.position = -9, risktable.cex = 0.7)
par(mfrow=c(2,1)) # Plot 1 surv.plot(fit.lung.arm.m, col = c("cadetblue2", "cadetblue"), time.unit = "month", segment.quantile = 0.5) # Plot 2 surv.plot(fit.lung.arm.m, col = c("cadetblue2", "cadetblue"), time.unit = "month", segment.timepoint = 6)
# Ensure that old settings are restored when the function exits par(users_default_setting)
split.screen()
split.screen(c(2,1)) screen(1) surv.plot(fit.lung.arm.m, time.unit = "month", segment.quantile = 0.5, segment.confint = FALSE) screen(2) surv.plot(fit.lung.arm.m, time.unit = "month", segment.quantile = 0.75, segment.confint = FALSE) close.screen(all = TRUE)
The following examples show how a figure can be exported as png file for a report.
png(file = file.path("kaplan_meier_plot.png"), width = 20, height = 14, units = "cm", res = 300) surv.plot(fit.lung.arm.m, risktable.name.position=-4, risktable.title.position=-4) dev.off()
If a bigger font size is needed then this can be done efficiently by choosing a different size of the output file.
png(file = file.path("kaplan_meier_plot_big_font.png"), width = 20*0.7, height = 14*0.7, units = "cm", res = 300) surv.plot(fit.lung.arm.m, ylab = "Estimated survival \n (probability)", risktable.name.position=-6.5, risktable.title.position=-6.5) dev.off()
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.