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

# Data structure

In the Graphy SDK, your **data** and your **spec** are separate. Data is a plain table you pass to `GraphProvider` as the `data` prop; the spec references that table's columns by key through its [mapping](/sdk-next/quickstart#2-render-your-first-chart). Keeping them apart means the same spec can be re-used across datasets, and the same data can be drawn several ways.

## Basic structure

Data is a table with explicitly defined columns and rows:

```tsx theme={null}
const data = {
  columns: [
    { key: 'category', label: 'Category' },
    { key: 'value', label: 'Value' },
  ],
  rows: [
    { category: 'A', value: 100 },
    { category: 'B', value: 200 },
    { category: 'C', value: 150 },
  ],
};
```

The `columns` array defines the shape; `rows` holds the values.

## Referencing columns from a spec

A spec never contains data — it names the columns it needs through its mapping. The variable strings you pass to `mapping` (or the `createSpec` shorthand) are column `key`s:

```tsx theme={null}
const spec = pipe(
  // 'category' and 'value' are column keys
  createSpec({ x: 'category', y: 'value' }),
  geom.bar(),
  scale.x(),
  scale.y()
);
```

Because the binding is by key, the `label` on a column is purely presentational — it's what shows in axes, legends and tooltips. If you omit it, the key is used.

<Note>
  Row keys must exactly match a column `key`. Values under keys with no matching
  column definition are ignored.
</Note>

## Value format detection

Graphy inspects each column's values and infers a **value format** — this drives how a scale interprets the column and how the renderer formats it. The first match wins.

### Numbers

Numbers and numeric strings are detected as quantitative values. Thousands separators, decimals and magnitude suffixes are supported:

```tsx theme={null}
{ category: 'A', value: 100 }
{ category: 'B', value: '1,250.50' }
{ category: 'C', value: '2.5m' }       // k, m, b, t suffixes supported
```

### Dates

Date-like strings are parsed automatically across a wide range of formats — ISO dates, named months and locale-specific orderings:

```tsx theme={null}
{ date: '2024-01-15', value: 100 }           // YYYY-MM-DD
{ date: 'January 2024', value: 200 }         // month name + year
{ date: '15/01/2024', value: 150 }           // DD/MM/YYYY (en-GB, the default locale)
{ date: '01/15/2024', value: 150 }           // MM/DD/YYYY (en-US)
{ date: '2024-01-15T12:30:00Z', value: 300 } // ISO datetime
{ date: 'Q1 2024', value: 400 }              // quarter
```

Short month names (`Jan`, `Feb`) are recognised alongside full names. Set `data._metadata.parsingLocale` to control whether ambiguous dates like `01/02/2024` are read as DD/MM (`en-GB`) or MM/DD (`en-US`).

**Weekly date ranges** are also detected — values like `1 Feb – 7 Feb` or `1 Feb 2024 – 7 Feb 2024` representing 7-day spans.

### Percentages

Values with a `%` suffix are detected as percentages:

```tsx theme={null}
{ category: 'Desktop', share: '64.5%' }
{ category: 'Mobile', share: '28.3%' }
```

### Currencies

Currency-formatted strings are parsed with automatic symbol recognition. Supported symbols: `$`, `€`, `£`, `¥`, `₹`, `₱`, `₩`, `₪`, `₫`, `₽`, `฿`, `₦`, `₺`, `zł`, `kr`, `Fr`, `R`, `R$`, `Rp`, `RM`, `د.إ`, `﷼`, `Ch$`, `NT$`, `HK$`, `S$`, `A$`, `C$`, `NZ$`, `MX$`.

```tsx theme={null}
{ product: 'A', price: '$1,250' }
{ product: 'B', price: '£2,450.50' }
{ product: 'C', price: '€3,000' }
```

Symbols can appear as prefix or suffix, and negative values are supported (`-$100`, `$-100`).

### Text

Any value that doesn't match the above is treated as text (categorical data).

## How formats reach scales

The inferred format feeds the scale you declare for each aesthetic. Calling a position scale bare (`scale.x()`) lets the engine choose a scale type from the column's format:

* A **text** column → a discrete (band) scale.
* A **numeric**, **percentage** or **currency** column → a continuous scale.
* A **date** column → a temporal scale.

You can always override the inference with an explicit method — `scale.x.continuous()`, `scale.x.discrete()`, `scale.x.datetime()` — when you want to force a particular treatment. See the [line chart guide](/sdk-next/graph-types/line) for worked examples.

## Missing values

Use `null` for a missing cell. How a geom treats gaps is geom-specific — for lines, the `missingValues` param chooses whether to break the path, bridge it, or treat the gap as zero.

```tsx theme={null}
rows: [
  { month: 'Jan', revenue: 12000 },
  { month: 'Feb', revenue: null }, // missing
  { month: 'Mar', revenue: 18000 },
];
```

## Schema

<ParamField path="data.columns" type="Column[]" required>
  Array of column definitions. Each column defines the structure and metadata
  for a data field.
</ParamField>

<ParamField path="columns[].key" type="string" required>
  Unique, stable identifier for the column. Must match the keys used in `rows`
  objects, and is what a spec's mapping references.
</ParamField>

<ParamField path="columns[].label" type="string" optional>
  Human-readable label shown in the UI (axis labels, legends, tooltips).
  Defaults to the `key` if not provided.
</ParamField>

<ParamField path="data.rows" type="Row[]" required>
  Array of data rows. Each row is an object with keys matching the column keys.
  Values can be a `string`, `number`, `Date` or `null`.
</ParamField>

<ParamField path="data._metadata" type="object" optional>
  Optional metadata for parsing and advanced data processing.

  <Expandable title="properties">
    <ParamField path="parsingLocale" type="'en-GB' | 'en-US'" optional>
      Locale used to infer column types and parse values (dates, numbers). Defaults to `'en-GB'`.

      * `'en-GB'`: DD/MM/YYYY date format
      * `'en-US'`: MM/DD/YYYY date format
    </ParamField>

    <ParamField path="sortBy" type="object" optional>
      Sort configuration: `{ columnKey: string; direction: 'asc' | 'desc' }`.
    </ParamField>

    <ParamField path="groupByTimeUnit" type="'year' | 'quarter' | 'month' | 'week' | 'day'" optional>
      Time unit for grouping time-based data.
    </ParamField>

    <ParamField path="rollingDateFilter" type="object" optional>
      Rolling date filter: `{ timeUnit: 'year' | 'quarter' | 'month' | 'week' | 'day'; value: number }` — e.g. the
      last 30 days is `{ timeUnit: 'day', value: 30 }`.
    </ParamField>
  </Expandable>
</ParamField>
