library(canvasXpress)
set.seed(1)
n <- 200
arm <- sample(c("Treatment", "Control"), n, replace = TRUE)
base <- ifelse(arm == "Treatment", 60, 40) # treatment survives longer
time <- round(rexp(n, rate = 1 / base), 1)
cens <- rbinom(n, 1, 0.3) # 1 = censored, 0 = event
subj <- paste0("Subj", seq_len(n))Kaplan–Meier survival curves in R with canvasXpress
Kaplan–Meier curves summarize time-to-event data: the step-down survival probability over time, usually split by a group like treatment arm. In this post we build one in R with canvasXpress. canvasXpress computes the survival steps for you — you hand it a time column and a censoring column, and it does the rest — and the result is an interactive figure that serializes to one portable, reproducible spec.
A survival table
Swap this simulated block for your own data: you need, per subject, a follow-up time, a censoring indicator (1 = censored, 0 = event), and the grouping variable.
canvasXpress reads two columns for a Kaplan–Meier plot — the time and its matching censor flag — plus a per-subject annotation carrying the group.
y <- data.frame(
"Time" = time,
"Time-Censor" = cens,
row.names = subj,
check.names = FALSE
)
varAnnot <- data.frame(Arm = arm, row.names = subj, check.names = FALSE)The plot
The key is graphType = "KaplanMeier" and passing both columns to xAxis (time first, then the censor column). colorBy stratifies the curves.
canvasXpress(
data = y,
varAnnot = varAnnot,
graphType = "KaplanMeier",
xAxis = list("Time", "Time-Censor"),
colorBy = "Arm",
title = "Overall survival by treatment arm",
xAxisTitle = "Time (weeks)",
yAxisTitle = "Probability of survival",
invertCensored = TRUE, # draw censoring ticks
showKMConfidenceIntervals = FALSE,
width = 650,
height = 500
)Hover any step for the time and survival probability; the tick marks are censored subjects. Because canvasXpress derives the survival curve from the raw table, you never precompute the steps — and the same spec re-renders the identical curve from Python or JavaScript.
A common gotcha
The survival transform only fires with graphType = "KaplanMeier". If you use a bare "Scatter2D" you’ll get a raw scatter of the censor values instead — survival probability is computed by the engine, it is not a column in your data.
Next
- Interactive volcano plots in R
- Gallery: https://canvasxpress.org/examples.html
- Package & issues: https://github.com/neuhausi/canvasXpress