Observable Plot boxplot: customize rule percentiles

Here is the doc for the boxplot that I am trying to customize: Box mark | Observable Plot

The doc states that it contains “a rule to represent the extreme values (not including outliers)”. However, I want to the rule to extend to the 5th and 95th percentiles of the dataset.

Plot.plot({
  y: {
    grid: true,
    inset: 6
  },
  marks: [
    Plot.boxY(morley, {x: "Expt", y: "Speed", /*anything I can add here?*/})
    /*or do something here?*/
  ]
})

How can I achieve that? I’m happy with whatever approach that works, including using D3.

Thanks.

You can create your own composite mark by following what we do for boxplot, but tweaking it to use p05 and p95 as boundaries for the rule and the dot marks:

function boxY_5_95(
  data,
  {
    y,
    x = null,
    fill = "#ccc",
    fillOpacity,
    stroke = "currentColor",
    strokeOpacity,
    strokeWidth = 2,
    sort,
    ...options
  } = {}
) {
  const group = x != null ? Plot.groupX : Plot.groupZ;
  return Plot.marks(
    Plot.ruleX(
      data,
      group(
        { y1: "p05", y2: "p95" },
        { x, y, stroke, strokeOpacity, ...options }
      )
    ),
    Plot.barY(
      data,
      group({ y1: "p25", y2: "p75" }, { x, y, fill, fillOpacity, ...options })
    ),
    Plot.tickY(
      data,
      group(
        { y: "p50" },
        { x, y, stroke, strokeOpacity, strokeWidth, sort, ...options }
      )
    ),
    Plot.dot(
      data,
      Plot.mapY(
        (v) => {
          v = d3.sort(v);
          const p05 = d3.quantileSorted(v, 0.05);
          const p95 = d3.quantileSorted(v, 0.95);
          return v.map((d) => (d > p05 && d <= p95 ? NaN : d));
        },
        { x, y, z: x, stroke, strokeOpacity, ...options }
      )
    )
  );
}
1 Like