> ## Documentation Index
> Fetch the complete documentation index at: https://docs.graphy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Error bar

export const GraphEmbed = ({src, story, aspectRatio, borderRadius = '14px', args}) => {
  const STORYBOOK_BASE_URL = typeof window !== 'undefined' && window.location.hostname === 'localhost' ? 'http://localhost:6006' : 'https://storybook-sdk.vercel.app';
  function buildArgsString(args) {
    if (!args) return '';
    const encodeValue = value => {
      if (value === null) return '!null';
      if (value === undefined) return '!undefined';
      if (typeof value === 'boolean') return `!${value}`;
      if (typeof value === 'number') return String(value);
      return encodeURIComponent(String(value));
    };
    const properties = Object.entries(args).map(([key, value]) => `${key}:${encodeValue(value)}`).join(';');
    return `&args=${properties}`;
  }
  const resolvedSrc = story ? `${STORYBOOK_BASE_URL}/iframe.html?id=${story}&viewMode=story&embed=1&globals=mode:readonly${buildArgsString(args)}` : src;
  const resolvedAspectRatio = aspectRatio ?? (story ? '16 / 9' : '10 / 6');
  return <iframe src={resolvedSrc} loading="lazy" allowfullscreen="true" style={{
    width: '100%',
    aspectRatio: resolvedAspectRatio,
    border: 'none',
    borderRadius,
    colorScheme: 'light'
  }} />;
};

An error bar shows the uncertainty around an estimate: a line from the lower end of an interval to its upper end, with a cap across each end. It is drawn over the bars or points that show the estimates, one error bar per category and group.

<Warning>
  The error bar ships as its own package, `@graphysdk/geom-errorbar`, on a `0.x`
  version line. While it is on `0.x`, a minor release may change its aesthetics,
  params, style or look; pin a caret range (`^0.1.0`) to take only patches. Tell
  us what works and what doesn't on [Discord](https://discord.gg/yGmYCjkSr).
</Warning>

## Install

```bash theme={null}
npm install @graphysdk/geom-errorbar
```

It peer-depends on `@graphysdk/react` and nothing else. Register the plugin once with `createGraphyKit`, which adds `kit.geom.errorbar()` and binds `kit.GraphProvider` to the same plugin:

```tsx theme={null}
import {
  createGraphyKit,
  GraphRenderer,
  highlight,
  styles,
} from '@graphysdk/react';
import { errorbar } from '@graphysdk/geom-errorbar';

const kit = createGraphyKit({ plugins: [errorbar] });
```

## Over a bar chart

Pass one row per estimate. Map the category to `x`, the estimate to `y`, and the interval's two ends to `lower` and `upper`. Add the error bar as a second layer after the bars, so it draws on top of them:

<GraphEmbed story="chart-types-errorbar--grouped-bars" args={{ orientation: 'vertical', interval: 'confidence interval' }} />

```tsx theme={null}
const data = {
  columns: [
    { key: 'treatment' },
    { key: 'age' },
    { key: 'days', label: 'Mean days' },
    { key: 'ciLow' },
    { key: 'ciHigh' },
    { key: 'se' },
  ],
  rows: [
    {
      treatment: 'Placebo',
      age: 'Under 50',
      days: 12,
      ciLow: 10.5,
      ciHigh: 13.5,
      se: 0.8,
    },
    {
      treatment: 'Placebo',
      age: '50 and over',
      days: 15,
      ciLow: 12,
      ciHigh: 18,
      se: 1.5,
    },
    // …
  ],
};

const spec = kit.pipe(
  kit.createSpec({ x: 'treatment', y: 'days', color: 'age' }),
  kit.geom.bar(),
  kit.geom.errorbar({ aes: { lower: 'ciLow', upper: 'ciHigh' } }),
  kit.scale.x.discrete(),
  kit.scale.y.continuous()
);

export function Recovery() {
  return (
    <kit.GraphProvider spec={spec} data={data}>
      <GraphRenderer />
    </kit.GraphProvider>
  );
}
```

A category holding several groups dodges its error bars across the band, the same way a grouped bar chart dodges its bars, so each error bar stands on the centre of its own group's bar. The error bars line up with bars of the default width; a bar with its own `width` param does not move its error bar with it.

The value axis fits the intervals. On its own, an error bar does not hold zero on the axis, because an interval shows where an estimate may lie, not a length. Under bars, the bars hold it.

## An interval, or an error

Map the interval in one of two ways:

| Mapping | The interval |
| - | - |
| `lower` and `upper` | From one end to the other, as written. Either order works. |
| `error` | From `y - error` to `y + error`, such as a standard error. |

Map one or the other. Mapping `error` together with either end is an error, and so is mapping only one end. A row with a missing end, a missing estimate or a negative error draws no error bar.

<GraphEmbed story="chart-types-errorbar--grouped-bars" args={{ orientation: 'vertical', interval: 'standard error' }} />

```tsx theme={null}
kit.geom.errorbar({ aes: { error: 'se' } });
```

## Over points

Draw the error bar first and a point over it for a dot-and-whisker chart. Here it is flipped:

<GraphEmbed story="chart-types-errorbar--dot-and-whisker" args={{ orientation: 'horizontal' }} />

```tsx theme={null}
const spec = kit.pipe(
  kit.createSpec({ x: 'region', y: 'score' }),
  kit.geom.errorbar({
    aes: { lower: 'low', upper: 'high' },
    params: { width: 0.3 },
  }),
  kit.geom.point(),
  kit.scale.x.discrete(),
  kit.scale.y.continuous(),
  kit.coord.flip()
);
```

Points are not dodged, so a dot-and-whisker chart takes one group per category.

## Horizontal

Add `kit.coord.flip()`. The categories move to the vertical axis and the intervals run across.

<GraphEmbed story="chart-types-errorbar--grouped-bars" args={{ orientation: 'horizontal', interval: 'confidence interval' }} />

## Caps

`width` is the geom's one param: how much of its share of the band each cap spans, from `0` to `1`. It is `0.5` by default. `0` draws a bare line with no caps. A value outside `[0, 1]` warns with `INVALID_GEOM_PARAM` and falls back to `0.5`.

```tsx theme={null}
kit.geom.errorbar({
  aes: { lower: 'ciLow', upper: 'ciHigh' },
  params: { width: 0 },
});
```

## Styling

The line and its caps are one observation, styled with `kit.style.geom.errorbar()`:

| Properties | Default |
| - | - |
| `stroke`, `alpha`, `strokeWidth`, `dashArray` | The primary text colour, 1.5px |

<GraphEmbed story="chart-types-errorbar--styled" args={{ orientation: 'vertical', width: 0.8 }} />

```tsx theme={null}
const spec = kit.pipe(
  // …
  styles({
    defaults: [
      kit.style.geom.bar({ fillAlpha: 0.35 }),
      kit.style.geom.errorbar({ stroke: '#1f1e1b', strokeWidth: 2.5 }),
    ],
  })
);
```

Error bars are drawn in the primary text colour whether or not `color` is mapped. A chart usually maps `color` for all its layers, so an error bar in its bar's colour would disappear where it crosses the bar. `color` still groups and dodges the error bars. A border set with `style.geom.bar()` stays off the error bars, and so does a chart-wide `style.geom()` fill, overlay or shadow. A chart-wide `stroke`, `alpha`, `strokeWidth` or `dashArray` does reach them, so they dim with the rest of the chart. To colour a group's error bars, pick them out with `where`:

```tsx theme={null}
kit.style.geom.errorbar(
  { stroke: '#b84737' },
  { where: { variable: 'age', eq: 'Under 50' } }
);
```

## The interval variables

The geom writes the two ends of each interval as variables you can name in a `highlight` or an entry's `where`. It writes them whichever way you mapped the interval, in `y`'s units and value format:

| Variable | Value |
| - | - |
| `errorbarLower` | The lower end of the interval |
| `errorbarUpper` | The upper end of the interval |

## Hover and highlight

An error bar attaches to the layer before it, the bars or points it stands on. It never takes the hover from them: hovering a bar emphasises it and its own error bar, and the tooltip gives each group's interval on an `Interval` row under the row of its own bar, which names the group:

```
Placebo
● Under 50     12
I Interval     10.5 – 13.5
● 50 and over  15
I Interval     12 – 18
```

Drawn alone, as a forest plot, the error bar answers the hover itself, and in a grouped chart the one under the cursor answers, not its whole category. To attach it to another layer, pass that layer's `id` as `attachTo`; `attachTo: false` lets it take the hover on its own.

With `config({ tooltip: { mode: 'observation' } })`, hovering a bar lists only that bar and its own interval. With no `color` mapped, the row is named `Interval`. Rename it through the layer's `tooltip`, as `{ Interval: { title: '95% CI' } }`.

A highlight raises each matched error bar. The bars have no `errorbarUpper` column, so a highlight on it reaches only the error bars. A highlight on a column the bars share raises both layers:

<GraphEmbed story="chart-types-errorbar--highlight" args={{ orientation: 'vertical', target: 'under 50' }} />

```tsx theme={null}
const spec = kit.pipe(
  // …
  highlight({ variable: 'age', eq: 'Under 50' })
);
```

## Not yet supported

* **The editor's chart-type picker and the chart agent.** In editable mode the error bar renders, hovers, selects and takes annotations on its upper end, but it is authored in code.
* **A continuous category axis.** The categories are always discrete, so error bars over a line on a time axis are not drawn yet.
* **Computing the interval from raw rows.** Pass the estimate and its interval; the geom does not compute a mean or a confidence interval from raw values.
* **Data labels.** `dataLabels` prints nothing on an error bar yet.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.