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

# Transforms

A **transform** reshapes the data before it's charted — pivoting columns into rows, filtering, sorting, aggregating, or adding a constant column. Transforms run before [mappings](/sdk-next/concepts/mappings) are read, so a mapping can reference columns a transform produced.

```tsx theme={null}
import { transform } from '@graphysdk/viz-engine';

pipe(
  createSpec(),
  transform.reshape({
    reshape: ['revenue', 'profit'],
    keyName: 'metric',
    valueName: 'amount',
  }),
  mapping({ x: 'month', y: 'amount', color: 'metric' }),
  geom.line(),
  scale.x(),
  scale.y(),
  scale.color.palette()
);
```

## Reshape: wide to long

The most common transform. Many datasets are **wide** — one column per series:

```tsx theme={null}
{ month: 'Jan', revenue: 12000, profit: 3000 }
```

But a mapping wants a **long** shape — one row per series, with a category column to split on:

```tsx theme={null}
{ month: 'Jan', metric: 'revenue', amount: 12000 }
{ month: 'Jan', metric: 'profit',  amount: 3000 }
```

`transform.reshape` pivots wide to long so you can map the new category column to `color`:

<ParamField path="reshape" type="string[]">
  The numeric columns to collapse into rows. Defaults to all numeric columns.
</ParamField>

<ParamField path="keep" type="string[]">
  Columns to carry through unchanged. Defaults to all categorical/temporal
  columns.
</ParamField>

<ParamField path="keyName" type="string" default="key">
  Name of the output column holding the original column names.
</ParamField>

<ParamField path="valueName" type="string" default="value">
  Name of the output column holding the values.
</ParamField>

## Other transforms

| Transform                                             | Purpose                                             |
| ----------------------------------------------------- | --------------------------------------------------- |
| `transform.filter({ variableName, operator, value })` | Keep only rows matching a comparison                |
| `transform.sort({ variableName, direction })`         | Order rows by a column                              |
| `transform.aggregate({ groupby, operations })`        | Group and summarise (sum, mean, …) into new columns |
| `transform.constant({ variableName, type, value })`   | Add a column with the same value on every row       |

```tsx theme={null}
transform.filter({ variableName: 'revenue', operator: 'gt', value: 0 });
transform.sort({ variableName: 'revenue', direction: 'desc' });
transform.aggregate({
  groupby: ['region'],
  operations: [{ op: 'sum', variableName: 'revenue', as: 'total' }],
});
```

## Spec-level vs layer-level

Piping a transform into the spec applies it to **all** layers. To reshape the data for a single geom — for example, a line overlay that needs a different shape than the bars beneath it — pass `transforms` on that geom:

```tsx theme={null}
geom.line({
  transforms: [
    transform.aggregate({
      groupby: ['month'],
      operations: [{ op: 'sum', variableName: 'revenue', as: 'total' }],
    }),
  ],
  aes: { y: 'total' },
});
```

## Next

* [Statistics](/sdk-next/advanced/statistics) — per-layer summaries that run after transforms
* [Mappings & aesthetics](/sdk-next/concepts/mappings) — reference the columns a transform produces
