Public
Edited
May 19
Insert cell
Insert cell
survivalData = [
{ id: 1, studyEntryAge: 68, studyExitAge: 69.5, time: 1.5, event: 1 },
{ id: 2, studyEntryAge: 53, studyExitAge: 56, time: 3.0, event: 0 },
{ id: 3, studyEntryAge: 61, studyExitAge: 64.5, time: 3.5, event: 1 },
{ id: 4, studyEntryAge: 50, studyExitAge: 54, time: 4.0, event: 1 },
{ id: 5, studyEntryAge: 64, studyExitAge: 70, time: 6.0, event: 0 },
{ id: 6, studyEntryAge: 56, studyExitAge: 62.5, time: 6.5, event: 1 },
{ id: 7, studyEntryAge: 66, studyExitAge: 73, time: 7.0, event: 1 },
{ id: 8, studyEntryAge: 63, studyExitAge: 71.5, time: 8.5, event: 0 },
{ id: 9, studyEntryAge: 55, studyExitAge: 64, time: 9.0, event: 1 },
{ id: 10, studyEntryAge: 50, studyExitAge: 60, time: 10.0, event: 0 }
]
Insert cell
survivalData
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = manualKMEstimation(survivalData)
Insert cell
data
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
kmPlot(survivalData)
Insert cell
Insert cell
Insert cell
Insert cell
function manualKMEstimation(data) {
// Sort by time
const sorted = data.slice().sort((a, b) => a.time - b.time);

// Get unique time points
const timePoints = [...new Set(sorted.map(d => d.time))];

let nAtRisk = data.length;
let cumulativeSurvival = 1;
const table = [];

for (const t of timePoints) {
const eventsAtT = sorted.filter(d => d.time === t && d.event === 1).length;
const censoredAtT = sorted.filter(d => d.time === t && d.event === 0).length;

if (eventsAtT > 0) {
const conditionalSurvival = (nAtRisk - eventsAtT) / nAtRisk;
cumulativeSurvival *= conditionalSurvival;
table.push({
time: t,
events: eventsAtT,
censored: censoredAtT,
atRisk: nAtRisk,
conditionalSurvival: conditionalSurvival.toFixed(3),
cumulativeSurvival: cumulativeSurvival.toFixed(3)
});
}

nAtRisk -= eventsAtT + censoredAtT;
}

return table;
}

Insert cell
function kmEstimate(data) {
const sorted = data.slice().sort((a, b) => a.time - b.time);

let n = sorted.length;
let survival = 1;
const curve = [{time: 0, survival: 1}];

for (const {time, event} of sorted) {
if (event === 1) {
const d = 1;
survival *= (n - d) / n;
curve.push({time, survival});
}
// either event or censored -> one less at risk
n -= 1;
}
return curve;
}
Insert cell
function kmPlot(data) {
const curve = kmEstimate(data);
return Plot.plot({
y: {label: "Survival"},
marks: [
Plot.lineY(curve, {
x: "time",
y: "survival",
stroke: "steelblue",
curve: "step-after"
}),
Plot.ruleY([0.5], {strokeDasharray: "4,4", strokeOpacity: 0.6}) // median guide
]
});
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more