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.tsxAbout
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.
<BoxPlot
series={series}
horizontal={false}
valueFormat={(ms) => `${ms}ms`}
/>Examples
Latency by region
<BoxPlot series={byRegion} valueFormat={(ms) => `${ms}ms`} />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'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| series | BoxSeries[] | required | { name, values? } for raw samples, or { name, summary } when only the five numbers came back from the warehouse. |
| height | number | 260 | — |
| domain | [number, number] | — | Fixed axis bounds. Defaults to the data, padded. |
| horizontal | boolean | false | Rows instead of columns. Better when the series names are long. |
| valueFormat | (value: number) => string | DEFAULT_FORMAT | — |
| whisker | number | 1.5 | Whisker reach, in IQRs. 1.5 is Tukey's convention. |
| showOutliers | boolean | true | — |
| emptyLabel | string | 'No data.' | — |
| label | string | 'Box plot' | — |
| from | not 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.
| Field | Type | Description |
|---|---|---|
| min | number | — |
| q1 | number | — |
| median | number | — |
| q3 | number | — |
| max | number | — |
| outliers? | number[] | — |
BoxSeries
| Field | Type | Description |
|---|---|---|
| name | string | — |
| values? | number[] | Raw samples. Ignored when summary is given. |
| summary? | BoxSummary | — |
| color? | string | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| whiskers | 1.5 × IQR | Tukey, not min/max. Whiskers to the extremes let one bad sample stretch the chart and fold outliers into the range, making them invisible. |
| quantiles | interpolated | R type-7 / numpy default, so the numbers match your analytics stack rather than disagreeing by a few milliseconds. |