---
warning: DO NOT CHANGE THIS MANUALLY, THIS IS GENERATED BY https://github.com/duckdb/community-extensions repository, check README there
title: duckfn_kuva
excerpt: |
DuckDB Community Extensions
Statistical plotting in SQL - a thin wrapper around the Rust library kuva, with the statistical semantics of seaborn and ggplot2 but no Python or R environment, on every platform DuckDB runs (console, JVM, Python, Node, browser)
extension:
name: duckfn_kuva
description: Statistical plotting in SQL - a thin wrapper around the Rust library kuva, with the statistical semantics of seaborn and ggplot2 but no Python or R environment, on every platform DuckDB runs (console, JVM, Python, Node, browser)
version: 0.3.0
language: Rust
build: cargo
license: MIT
requires_toolchains: "rust;python3"
maintainers:
- shijianjs
repo:
github: shijianjs/duckfn-kuva
ref: 5fd677c70d5572f61eab5d44ce9a157708d15486
ref_next: 5fd677c70d5572f61eab5d44ce9a157708d15486
docs:
hello_world: |
-- One function: a JSON chart spec in, an SVG document out
SELECT kuva_render('{"series":[{"type":"scatter","data":[{"x":1,"y":2},{"x":3,"y":4},{"x":5,"y":3}]}]}');
--
-- Several series overlay on one set of axes
SELECT kuva_render('{"series":[{"type":"line","data":[{"x":0,"y":1},{"x":1,"y":2},{"x":2,"y":1.5}],"legend":"signal"},{"type":"scatter","data":[{"x":0,"y":1.2},{"x":1,"y":1.8},{"x":2,"y":1.6}],"legend":"observed"}]}');
--
-- Or a multi-panel figure grid
SELECT kuva_render('{"figure":{"rows":1,"cols":2,"panels":[{"series":[{"type":"scatter","data":[{"x":1,"y":2},{"x":2,"y":3}]}]},{"series":[{"type":"histogram","values":[1,2,2,3,3,3,4],"bins":4}]}]}}');
--
-- The chart config and the data can be separate: pass a dataset registry as the second argument
SELECT kuva_render('{"series":[{"type":"scatter","dataset":"d","encode":{"x":"a","y":"b"}}]}',
'{"d":[{"a":1,"b":2},{"a":3,"b":4}]}');
--
extended_description: |
Renders statistical charts from SQL. The extension wraps the Rust plotting library kuva (SVG backend
only) and exposes it through five scalar functions, so charts work anywhere DuckDB runs - the CLI, a
Python or R session, the JVM, or the browser build (DuckDB-Wasm) - from one and the same extension
binary. There is no matplotlib or ggplot2 in the loop, and no plotting environment to install.
| Function | Kind | Input -> output |
| --- | --- | --- |
| `kuva_render(spec)` | scalar | `VARCHAR` (a JSON chart spec) -> `VARCHAR` (an SVG document) |
| `kuva_render_terminal(spec)` | scalar | `VARCHAR` -> `VARCHAR` (terminal text: braille dots and ANSI colour) |
| `kuva_render_typst(spec)` | scalar | `VARCHAR` -> `VARCHAR` (Typst source: a `.typ` CETZ document for `typst compile`) |
| `kuva_render_file(spec)` | scalar | `VARCHAR` -> `VARCHAR` (the path of the SVG file it wrote; native builds only) |
| `kuva_version()` | scalar | `-> VARCHAR` (the kuva version the loaded build was compiled against) |
All four render functions take the same JSON spec, and all four also take an optional second argument: a JSON
object mapping dataset names to arrays of rows, so the chart config and the data can be built by
different things - the config by a host SDK, the data by SQL. A series names a dataset with
`dataset` and maps its columns with `encode` (roles like `x`, `y`, `value`, `name`, `group`); the
one-argument form is unchanged.
Their own options stay in the JSON: `kuva_render_terminal` reads a top-level `terminal` object
(`cols` / `rows` / `print`) and `kuva_render_file` reads a top-level `file` object (`dir` / `name` /
`open`), which is what lets one statement write a chart to disk and optionally open it in a browser.
`kuva_render_typst` has no options object: it returns Typst source (a CETZ vector drawing) that you
compile yourself with `typst compile`.
The JSON spec uses snake_case keys and describes the drawing rather than one particular chart. The
top-level keys are `title`, `x_axis`, `y_axis`, `grid`, `legend`, `theme`, `palette`, `font`,
`annotations`, `width`, `height` and `series` (or `figure` / `composite` instead of `series`). Every
`series` entry carries a `type` from the table
below and declares only the fields that type needs, so a scatter takes `data` as `{"x":…, "y":…}`
objects with optional `x_err` / `y_err`, a bar takes `categories` plus `values` (or several named
`series` for grouped and stacked bars), a histogram takes `values` with optional `bins` and a `kde`
overlay, a box takes `groups` of raw values, and a pie takes `slices`.
| Group | `type` values |
| --- | --- |
| Distributions | `histogram` `histogram2d` `density` `ridgeline` `ecdf` `qq` `box` `violin` `strip` `raincloud` `hexbin` `heatmap` |
| Relationships & correlation | `scatter` `line` `series` `band` `jointplot` `contour` `parallel` `polar` `ternary` `quiver` |
| Categorical & comparison | `bar` `pie` `waffle` `funnel` `pareto` `pyramid` `lollipop` `slope` `dot_plot` `dice_plot` `venn` `mosaic` `rose` `radar` `upset` |
| Time series | `stacked_area` `streamgraph` `candlestick` `waterfall` `horizon` `calendar` `gantt` `bump` |
| Statistical & model evaluation | `roc` `pr` `survival` `forest` `volcano` `manhattan` |
| Hierarchical & network | `treemap` `sunburst` `network` `sankey` `chord` `phylo` `clustermap` |
| 3D | `scatter3d` `surface3d` |
| Composite & utility | `brick` `synteny` `text` `legend_plot` |
JSON rather than a STRUCT is deliberate: the series of one figure are heterogeneous, and a STRUCT
list cannot hold a mix of them.
Composition works three ways. Several series in one `series` array are overlaid on a shared set of
axes - a line with its scatter points on top, for instance. A top-level `figure` with `rows`,
`cols` and `panels` switches to a multi-panel grid with shared axes and an optional shared legend.
A top-level `composite` object draws a genome-browser coverage figure or a population
allele-frequency figure (`type: coverage` or `brick_pop`); it is mutually exclusive with `series`
and `figure`, carries its own canvas size and does not read a dataset.
`theme` selects light, dark, minimal or solarized (or overrides individual colours), `palette`
selects one of the named palettes or an explicit colour list, and `annotations` adds reference
lines, shaded regions and text callouts. `grid` and `legend` cover the axis chrome, tick formats,
grayscale (accessibility) mode and legend placement; `width`, `height` and `scale` size the canvas.
A malformed spec fails the query with an actionable message rather than returning NULL, so a typo
is never silent - for example a bar whose `values` and `categories` differ in length is rejected
before anything is drawn.
Requires **DuckDB 1.3 or newer**: the extension is built against DuckDB 1.5.6 headers but declares
the C API floor it needs (v1.2.0), so one binary loads into 1.3.2 through 1.5.6 alike. The full
field reference and the development notes live in the repository:
.
extension_star_count: 0
extension_star_count_pretty: 0
extension_download_count: 778
extension_download_count_pretty: 778
image: '/images/community_extensions/social_preview/preview_community_extension_duckfn_kuva.png'
layout: community_extension_doc
---
### Compatibility Matrix
This table covers all DuckDB releases that have not reached [end-of-life](https://duckdb.org/release_calendar), including LTS releases, and shows the 2 most recent patch releases of each. Older patch releases are not listed, but the extension may be available for them as well.
| Platform | 1.4.4 | 1.4.5 | 1.5.5 | 1.5.6 | 2.0.0 |
| :--- | :---: | :---: | :---: | :---: | :---: |
| Linux AMD64 | ❌ | ❌ | ❌ | ✅ | ✅ |
| Linux ARM64 | ❌ | ❌ | ❌ | ✅ | ✅ |
| macOS AMD64 | ❌ | ❌ | ❌ | ✅ | ✅ |
| macOS ARM64 | ❌ | ❌ | ❌ | ✅ | ✅ |
| Windows AMD64 | ❌ | ❌ | ❌ | ✅ | ✅ |
| Windows ARM64 | ❌ | ❌ | ❌ | ❌ | ❌ |
| WebAssembly | ❌ | ❌ | ❌ | ✅ | ✅ |
### Installing and Loading
```sql
INSTALL {{ page.extension.name }} FROM community;
LOAD {{ page.extension.name }};
```
{% if page.docs.hello_world %}
### Example
```sql
{{ page.docs.hello_world }}```
{% endif %}
{% if page.docs.extended_description %}
### About {{ page.extension.name }}
{{ page.docs.extended_description }}
{% endif %}
### Added Functions
| function_name | function_type | description | comment | examples |
|----------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| kuva_render | scalar | Renders a complete chart described by a JSON string into an SVG document | The full-featured, JSON-based entry point: one JSON object describes canvas, axes, legend, annotations and a heterogeneous list of series; an optional second argument is a JSON object mapping dataset names to arrays of rows, which a series names with `dataset` and maps with `encode` | [SELECT kuva_render('{"series":[{"type":"scatter","dataset":"sales","encode":{"x":"revenue","y":"profit"{% raw %}}}{% endraw %}]}', '{"sales":[{"revenue":10,"profit":3},{"revenue":12,"profit":5}]}'); SELECT kuva_render('{"series":[{"type":"scatter","data":[{"x":1,"y":2},{"x":3,"y":4},{"x":5,"y":3}]}]}')] |
| kuva_render_file | scalar | Renders a chart described by a JSON string to an SVG file and returns the file path; the JSON's own `file` object sets the directory, the file name and whether to open it in a browser | Native builds only: writes the SVG to disk (default: a file in the system temp directory, created if the directory is missing) and optionally opens it in the system default browser; `file: {dir, name, open}` rides along in the JSON, and an optional second argument supplies the data as a JSON dataset registry | [SELECT kuva_render_file('{"series":[{"type":"scatter","dataset":"d","encode":{"x":"a","y":"b"{% raw %}}}{% endraw %}]}', '{"d":[{"a":1,"b":2},{"a":3,"b":4}]}'); SELECT kuva_render_file('{"file":{"dir":"/tmp/charts","name":"scatter.svg","open":true},"series":[{"type":"scatter","data":[{"x":1,"y":2},{"x":3,"y":4}]}]}')] |
| kuva_render_terminal | scalar | Renders a chart described by a JSON string as terminal text (braille dots and ANSI colour); the JSON's own `terminal` object sets the character grid and whether to print straight to stdout | Same JSON as kuva_render, different backend: `terminal: {cols, rows, print}` — a braille cell is 2x4 dots, defaults are 100x30, and `print: true` writes to stdout and returns NULL; an optional second argument supplies the data as a JSON dataset registry | [SELECT kuva_render_terminal('{"series":[{"type":"bar","dataset":"t","encode":{"name":"k","value":"v"{% raw %}}}{% endraw %}]}', '{"t":[{"k":"a","v":4},{"k":"b","v":7}]}'); SELECT kuva_render_terminal('{"terminal":{"cols":100,"rows":30},"series":[{"type":"line","data":[{"x":1,"y":2},{"x":2,"y":3}]}]}')] |
| kuva_render_typst | scalar | Renders a chart described by a JSON string as a Typst source document (a CETZ vector drawing) for `typst compile` | Same JSON as kuva_render, different backend: the result is a `.typ` document (page setup + an `@preview/cetz` import + CETZ primitives). kuva does not embed a Typst compiler, so run `typst compile` on the returned source to get PDF / SVG / PNG; an optional second argument supplies the data as a JSON dataset registry | [SELECT kuva_render_typst('{"series":[{"type":"bar","dataset":"t","encode":{"name":"k","value":"v"{% raw %}}}{% endraw %}]}', '{"t":[{"k":"a","v":4},{"k":"b","v":7}]}'); SELECT kuva_render_typst('{"series":[{"type":"line","data":[{"x":1,"y":2},{"x":2,"y":3}]}]}')] |
| kuva_version | scalar | Returns the kuva version this extension was compiled against | Useful when triaging an old figure: the SVG also records the version in a comment, but this answers it for the extension that is loaded right now | [SELECT kuva_version()] |
### Overloaded Functions
This extension does not add any function overloads.
### Added Types
This extension does not add any types.
### Added Settings
| name | description | input_type | scope | aliases | typed_value |
|----------------|---------------------------------------------------------|------------|--------|---------|-------------|
| temp_directory | Set the directory to which temporary files are written. | VARCHAR | GLOBAL | [] | post.db.tmp |