observablehq / plot

A concise API for exploratory data visualization implementing a layered grammar of graphics
https://observablehq.com/plot/
ISC License
4.4k stars 176 forks source link

Kaplan-Meier curve transform #2144

Open fgregg opened 2 months ago

fgregg commented 2 months ago

It would be great to have support of kaplan-meier curve, perhaps in the Plot.map family.

As the curve is basically a cumulative sum with some transformations, it shouldn't be too hard.

This code gives a pretty good approxmation:

const caseLength = [{ case_length: 1 }, { case_length: 11 }, { case_length: null }]

Plot.plot({
  y: { transform: (d) => 100 - d * 100 },
  marks: [
    Plot.ruleY([1]), // we want a rule at 0% but it will get flipped by the transform
    Plot.line(
      caseLength,
      Plot.normalizeY(
        (arr) => arr.length,
        Plot.mapY("cumsum", {
          x: "case_length",
          y: (d) => (d.case_length === null ? 0 : 1),
          curve: "step-before"
        })
      )
    )
  ]
})

If interested, I can work on something that extends Plot.map

Fil commented 2 months ago

It should be possible to simplify a little:

Plot.plot({
  y: { percent: true },
  marks: [
    Plot.ruleY([0]),
    Plot.line(
      caseLength,
      Plot.normalizeY(
        (arr) => arr.length,
        Plot.mapY("cumsum", {
          sort: "case_length",
          reverse: true,
          x: "case_length",
          y: (d) => (d.case_length === null ? 0 : 1),
          curve: "step-after"
        })
      )
    )
  ]
})

I think this is more for an example (or an essay explaining how it's used), rather than as a built-in transform?