> ## 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.

# Statistics

A **stat** summarises a layer's data before it's drawn. Instead of plotting raw rows, the layer plots a computed result — a count, a mean, or a fitted regression line. Stats are set per layer through a geom's `stat` option. A layer that names none runs its geom's default stat, which is `identity` (draw the data as-is) for every built-in geom.

```tsx theme={null}
import { stat } from '@graphysdk/react';

geom.line({ stat: stat.smooth({ method: 'linear' }) });
```

## Available stats

| Stat | Result |
| - | - |
| `stat.identity()` *(default)* | The data unchanged |
| `stat.count()` | The number of observations per x-value, per series |
| `stat.sum()` | The total of `y` per x-value, per series |
| `stat.mean()` | The mean of `y` across the whole layer, as a single observation |
| `stat.summary({ interval? })` | The mean or median of `y` per x-value, per series, with an optional interval around it |
| `stat.smooth({ method })` | A fitted regression curve through the points |

`count`, `sum` and `summary` group by x plus the layer's grouping variables, so they preserve the series a
reader is comparing. `mean` reduces the layer to one observation, which is why it pairs with
`geom.rule()` rather than with a bar or a line.

`count` computes `y` for you — mapping `y` alongside it is a `CONFLICTING_STAT_MAPPING` error.

Every stat also has a string shorthand, so `geom.bar({ stat: 'count' })` is the same as
`geom.bar({ stat: stat.count() })`. The names are `'identity'`, `'count'`, `'smooth'`, `'mean'`,
`'sum'` and `'summary'`.

## Trendlines with `smooth`

The `smooth` stat fits a regression through a layer's points — the basis for trendlines. Add it to a second `geom.line()` layer over your raw data:

```tsx theme={null}
pipe(
  createSpec({ x: 'date', y: 'sales' }),
  geom.point(), // the raw points
  geom.line({ stat: stat.smooth({ method: 'linear' }) }), // the trend
  scale.x(),
  scale.y()
);
```

`method` selects the regression family:

<ParamField path="method" type="'linear' | 'loess' | 'exponential' | 'logarithmic' | 'quadratic' | 'power' | 'polynomial'" default="linear">
  The regression method to fit. The object builder asks for it; the `'smooth'`
  string shorthand takes the default.
</ParamField>

<ParamField path="order" type="number" default="3">
  Polynomial order — only used when `method: 'polynomial'`.
</ParamField>

<ParamField path="bandwidth" type="number" default="0.3">
  Smoothing bandwidth — only used when `method: 'loess'`.
</ParamField>

```tsx theme={null}
stat.smooth({ method: 'polynomial', order: 4 });
stat.smooth({ method: 'loess', bandwidth: 0.5 });
```

## A mean reference line

`stat.mean()` reduces the layer to one observation, which is exactly what a rule needs. Pair the two
to draw a horizontal line at the layer's average:

```tsx theme={null}
pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.bar(),
  geom.rule({ stat: stat.mean() }), // a line at the mean revenue
  scale.x(),
  scale.y()
);
```

## Estimates and intervals with `summary`

The `summary` stat reduces `y` to one observation per x-value and series: an estimate of it and, if you ask for one, an interval around it. The estimate is mapped to `y`, and the interval's ends to `lower` and `upper`, which is what a geom drawing an interval reads.

```tsx theme={null}
pipe(
  createSpec({ x: 'treatment', y: 'recoveryDays', color: 'ageBand' }),
  geom.bar({ stat: stat.summary() }), // the mean per treatment and age band
  scale.x(),
  scale.y()
);
```

<ParamField path="estimate" type="'mean' | 'median'" default="mean">
  The value each x-value and series reduces to.
</ParamField>

<ParamField path="interval" type="'stderr' | 'stdev' | 'ci' | 'iqr' | 'range'">
  The interval around the estimate. It has no default: leave it out and the stat computes the estimate alone, so a chart
  that draws an interval always says which one it is.

  * `'stderr'`: the mean ± `mult` standard errors
  * `'stdev'`: the mean ± `mult` standard deviations
  * `'ci'`: a `level` confidence interval for the mean, from Student's t distribution
  * `'iqr'`: the first to the third quartile
  * `'range'`: the smallest value to the largest

  `'stderr'`, `'stdev'` and `'ci'` describe a mean, so they need `estimate: 'mean'`. `'iqr'` describes a median, so it needs `estimate: 'median'`.
</ParamField>

<ParamField path="level" type="number" default="0.95">
  The confidence level, between `0` and `1`. Only used with `interval: 'ci'`.
</ParamField>

<ParamField path="mult" type="number" default="1">
  How many standard errors or deviations each end sits from the mean. Only used
  with `interval: 'stderr'` or `'stdev'`.
</ParamField>

```tsx theme={null}
stat.summary({ interval: 'ci', level: 0.9 });
stat.summary({ interval: 'stderr', mult: 2 });
stat.summary({ estimate: 'median', interval: 'iqr' });
```

An x-value and series with a single value gets no interval, since one value has no spread to measure. A param outside what it can be fails with `INVALID_STAT_PARAM`.

## Setting a stat at runtime

Two [commands](/sdk-next/advanced/commands-and-history) reach the stat surface:

| Command | Params | What it does |
| - | - | - |
| `SetLayerStatCommand` | `layerId?`, `stat` | Sets one layer's stat |
| `SetStatLineCommand` | `line: 'none' \| 'trend' \| 'average'`, `layerId?`, `method?`, `group?`, `label?`, `removedLayer?` | The switch behind "show a trendline" and "show an average" — swapping one for the other is one undo entry |

## Custom stats

A plugin can contribute its own stat: a `Stat` subclass registered through `plugins`, which earns a typed builder beside the built-ins. A custom geom can also declare one as its `defaultStat`, so its layers summarise without naming it and registering the geom registers the stat. See [a summary is a stat](/sdk-next/extending/custom-geoms#a-summary-is-a-stat) for writing one, and [Extending](/sdk-next/extending/index) for the registration path.

## Next

* [Transforms](/sdk-next/advanced/transforms) — reshape data before it reaches a stat
* [Reference lines](/sdk-next/emphasis/reference-lines) — the rules `stat.mean()` draws
* [Geoms & layers](/sdk-next/concepts/geoms) — where the `stat` option lives


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