BiasStrip
"A Bland–Altman read: the difference between two measurement methods, plotted against their mean, with limits of agreement."
- encodes
- vertical position of the paired difference
- precision
- high
- nodes
- 1 per pair (≤ 40) + band + 2 lines
A new device tracks its reference closely and still reads about 2 units high. BiasStrip plots that offset: each dot is one pair placed at its mean and its difference, the dashed line is perfect agreement, the accent line is the measured bias, and the faint band is the limits of agreement. The y-axis is symmetric about zero, so a systematic offset reads as the whole cloud sitting above or below the line. Dots render at 75% opacity so overplot reads as density, and pairs beyond the limits are enlarged and re-inked so outliers read on shape, not color alone.
Install
import { BiasStrip } from "@microcharts/react/bias-strip";// pairs — real values under “Sample data” below<BiasStrip data={pairs} title="Device vs reference" />Sample data
// a ~+2 bias with noise and two pairs beyond the limits of agreementconst pairs = [ 1.8, 2.4, 1.5, 2.9, 2.1, 1.2, 2.6, 3.0, 1.9, 2.3, 6.5, 2.0, 1.7, 2.8, 2.2, -1.5, 2.5, 1.6, 2.7, 2.0,].map((d, i) => ({ a: i + d, b: i }));Try it
When to use it
Use it to check whether a new instrument, method, or model agrees with a reference, and for instrument drift in a KPI card. For unpaired samples use MicroScatter; for a single time series use Sparkline.
Two methods can correlate perfectly and still disagree by a constant offset. BiasStrip plots the difference, so that offset shows up as distance from the zero line where a correlation plot would hide it along the diagonal.
Sizing
Variants
The band is the classic ±1.96σ limits of agreement. limits widens or tightens it (2.58 for about 99%) without changing
what any mark means.
Edge cases
Empty data draws just the frame with "No data." as the summary. Under 5 pairs the limits aren't meaningful, so the band and bias line drop out and only the dots and the zero reference remain: the chart makes no interval claim at that sample size, and the mean difference is still stated in the summary. When the two methods agree perfectly the band collapses to a hair around the zero line rather than vanishing, so the measured result stays visible. Pairs with a non-finite value are dropped before the stats and the count. Past 40 pairs the dots are uniformly downsampled for display, while the bias and limits still use every pair.
Four homes
Accessibility
The accessible name is the bias plus the evidence-backed agreement share — "Bias +2.21 across 20 pairs; 90% within the
limits of agreement." — or the shorter "Bias +2 across 4 pairs." when there are too few pairs to draw limits.
Localizing carries the number format through: with locale="de-DE" the same chart reads "Bias +2,21 across 20 pairs;
90% within the limits of agreement." The interactive entry steps pairs ordered by mean and announces each pair's mean,
difference, and whether it clears the limits.
The interactive entry follows the shared interaction contract:
arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or
Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape.
On touch, a tap pins and a drag scrubs.
Props
| Prop | Type | Notes |
|---|---|---|
| data* | { a; b }[] | Paired measurements. |
| limits | number | k in bias ± k·σ (default 1.96 ≈ 95% limits of agreement). |
| label | "bias" | "none" | Seat-gated bias caption (default) or hidden. |
| r | number | Base dot radius, clamped [1, 3]. |
| animate | boolean | interactiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion. |
Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.