Box Plot

Quartiles, Tukey whiskers and outliers — the distribution an average hides. Quartiles use linear interpolation, so they agree with what your analytics stack reports.

src/components/ui/box-plot.tsx

About

From the component's own source, where it was written.

The shape of a distribution: quartiles, whiskers and outliers.

**The chart that averages hide behind.** A mean latency of 180 ms is compatible with "everything is 180 ms" and with "most requests are 40 ms and a tenth take two seconds", and those are different products. A box plot shows the spread, the skew and the tail in the space a single bar would take, which is why it is the right chart for latency, response times, prices and scores.

**Whiskers are Tukey's 1.5 × IQR, not the min and max.** Drawing whiskers to the extremes means one bad sample stretches the whole chart and the box collapses to a line — and, worse, it makes outliers invisible by folding them into the range. Here the whisker stops at the furthest point still within 1.5 IQR of the quartiles, and anything past it is drawn individually as the exception it is.

**Quartiles use linear interpolation** (the R type-7 / numpy default), so they agree with what your analytics stack reports rather than being a nearest-rank approximation that disagrees by a few milliseconds and starts an argument.

Pass raw samples in values and the statistics are computed; pass a precomputed summary when the data lives in a warehouse and only the five numbers came back.

Install

npx astralyx-ui add box-plot

Usage

import { BoxPlot } from '@/components/ui/box-plot'

<BoxPlot series={[{ name: 'us-east', values: latencies }]} />

Composer

Change the props and the source below follows.

Box plot
us-east
eu-west
ap-south
-48ms1737ms
Props
tsx
<BoxPlot
  series={series}
  horizontal={false}
  valueFormat={(ms) => `${ms}ms`}
/>

Examples

Latency by region

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

BoxPlot

Extends Omit<ComponentProps<'figure'>, 'height'>.

PropTypeDefaultDescription
seriesBoxSeries[]required{ name, values? } for raw samples, or { name, summary } when only the five numbers came back from the warehouse.
heightnumber260—
domain[number, number]—Fixed axis bounds. Defaults to the data, padded.
horizontalbooleanfalseRows instead of columns. Better when the series names are long.
valueFormat(value: number) => stringDEFAULT_FORMAT—
whiskernumber1.5Whisker reach, in IQRs. 1.5 is Tukey's convention.
showOutliersbooleantrue—
emptyLabelstring'No data.'—
labelstring'Box plot'—
fromnot SVG—Every part is an axis-aligned rectangle or line, and median thickness, cap widths and outlier dots stay exact in CSS pixels at any size.

Types

BoxSummary

The shape of a distribution: quartiles, whiskers and outliers.

FieldTypeDescription
minnumber—
q1number—
mediannumber—
q3number—
maxnumber—
outliers?number[]—

BoxSeries

FieldTypeDescription
namestring—
values?number[]Raw samples. Ignored when summary is given.
summary?BoxSummary—
color?string—

Behaviour

TopicDetailDescription
whiskers1.5 × IQRTukey, not min/max. Whiskers to the extremes let one bad sample stretch the chart and fold outliers into the range, making them invisible.
quantilesinterpolatedR type-7 / numpy default, so the numbers match your analytics stack rather than disagreeing by a few milliseconds.