Creating survival plot

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.

Setup and Loading Data


# 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)

Drawing basic survival plot

# Single arm
surv.plot(fit.lung.m)


# Two arm
surv.plot(fit.lung.arm.m)

Customisation of the survival plot

Colour, title and axis label

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)"
)

Legend position, name and title

# 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"
)

Axis limits, xticks and yticks

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)
)

Font size

Global adjustment

surv.plot(fit.lung.arm.m,
          legend.name = c("Male", "Female"),
          # Global adjustment
          cex = 1.3,
          risktable.name.position = -6,
          risktable.title.position = -6
)

Specific adjustment

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
)

Label position x and y axis

Shift x and y axis label

surv.plot(fit.lung.arm.m,
          legend.name = c("Male", "Female"),
          xlab.pos = 6,
          ylab.pos = 5
)

Margin area customisation


# 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
)

Time unit and y-axis unit

The parameter time.unit can be set as follows: "day", "week", "month","year".

Note the following:

# 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")
)

Drawing risk table

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.

Undisplay risk table

surv.plot(fit.lung.arm.m,
          legend.name = c("Male", "Female"),
          risktable = FALSE
)

Risktable position

# 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
)

Risktable title, colour and label name

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"
)

Risktable with censoring indication

surv.plot(fit.lung.arm.m,
          risktable.censoring = TRUE)

Drawing segment

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.

For a specific quantile

# 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
)

For a specific time point

#  Drawing a segment line at 12 months
surv.plot(fit.lung.arm.m,
          legend.name = c("Male", "Female"),
          time.unit = "month",
          segment.timepoint = 12
)

Customisation of the segment

Change position of segment annotation

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"
)

Segment title, font, size, and colour

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"
)

Segment lines for different time points / quantile

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"
)

Segment line type, line width and text spacing

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
)

Segment annotation short version

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"
)

Modify confidence interval

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
)

Include statistics

There are three options for the parameter stat to display statistics:

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"
)

Customisation of the statistics

Stat position, colour, text size, and text font

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
)

Stat with redfined reference arm and confidence level

surv.plot(fit.lung.arm.m,
          legend.name = c("Female","Male"),
          stat = "coxph_logrank",
          reference.arm = 2,
          stat.conf.int = 0.80
)

Stat with stratification

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"
)

Predefined theme options

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")

Multiple survival plots

We present to ways how to combine plots: via par(mfrow=c()) and via split.screen()

Multiple plots using par(mfrow=c())

# 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)

Multiple plots using split.screen()

split.screen(c(2,1))
#> [1] 1 2
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)

Export plots as png file

Figure output for a report

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()

Figure output for a poster

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()