Search Shortcut cmd + k | ctrl + k
anofox_visualization

Render charts and dashboards straight from SQL — the ggplot-rs grammar of graphics, emitted as SVG.

Maintainer(s): sipemu

Compatibility Matrix

This table covers all DuckDB releases that have not reached end-of-life, 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

INSTALL anofox_visualization FROM community;
LOAD anofox_visualization;

Example

-- One scalar plus convenience macros. Pass columns; get an SVG per group.
WITH sales AS (SELECT * FROM (VALUES ('app', 30), ('web', 22), ('api', 12)) t(ch, n))
SELECT anofox_bar(ch, n) FROM sales;          -- <svg> bar chart

-- Any chart kind via anofox_xy(..., kind := ...), or the raw JSON spec:
SELECT anofox_render('{"rows":[{"c0":"a","c1":3}],"roles":[[0,"XAXIS"],[1,"BARCHART"]]}');

About anofox_visualization

anofox_visualization renders charts as SVG from inside DuckDB, using the ggplot-rs grammar-of-graphics engine. This community build provides the rendering surface:

  • anofox_render(spec VARCHAR) -> VARCHAR — a JSON panel spec -> SVG
  • anofox_bar / anofox_line / anofox_scatter / anofox_area (x, y)
  • anofox_xy(x, y, kind := 'BARCHART', width := 640, height := 400)
  • anofox_xyc(x, y, series, kind := 'BARCHART_STACKED')

The convenience macros aggregate rows via list(), so SELECT anofox_bar(x, y) FROM t returns one SVG for the group. Chart kinds include bars/lines/areas/points, distributions (histogram, boxplot, violin), heatmaps, and WKT-geometry choropleth maps.

The full toolkit — an interactive in-browser dashboard builder, locked read-only dashboard serving, and a CLI — is available from a source build of the project at https://github.com/DataZooDE/anofox-visualization.

Added Functions

function_name function_type description comment examples
anofox_area macro Render an area chart of two columns as SVG. Shorthand for anofox_xy(x, y, kind := 'AREACHART'). NULL [SELECT anofox_area(x, y) FROM (VALUES ('Jan', 10), ('Feb', 20)) t(x, y)]
anofox_bar macro Render a bar chart of two columns as SVG. Shorthand for anofox_xy(x, y, kind := 'BARCHART'). NULL [SELECT anofox_bar(x, y) FROM (VALUES ('Jan', 10), ('Feb', 20)) t(x, y)]
anofox_contract_version scalar Returns the version of the anofox integration contract (output schemas and duckdb_functions() tags) whose tables the anofox_plot_* macros consume. NULL [anofox_contract_version()]
anofox_line macro Render a line chart of two columns as SVG. Shorthand for anofox_xy(x, y, kind := 'LINECHART'). NULL [SELECT anofox_line(x, y) FROM (VALUES ('Jan', 10), ('Feb', 20)) t(x, y)]
anofox_plot macro Plot any anofox contract table (contract version 1), choosing the chart from its columns: term+estimate -> terms, curve_type -> curve, yhat -> prediction, fitted+residual -> diagnostics 2x2, metric+value -> summary, check_name|thresholds + status + value -> checks, partial_residual -> partial (component + residual), fold_id + (split | test_start | train_end) -> cv (provisional), resource+start+end -> schedule (Gantt), component+value | trend -> decomposition, p_value/test_id -> tests, row+col | column_a+column_b -> matrix, kind|severity + ds|period -> events, (bin|wave)+item+size -> assignment, option+probability -> distribution. Use the specific anofox_plot_* macro for options. NULL [SELECT anofox_plot(c) FROM (VALUES ('x1', 0.5, 0.2, 0.8)) c(term, estimate, conf_low, conf_high)]
anofox_plot_assignment macro An optimizer's item -> bin/wave assignment from an assignment table (anofox-optimize's pack_* / wave_* as a long-form bridge): one row per item (model_id, bin | wave, item, size, capacity). Items are stacked per bin/wave (CATEGORY: item, BARCHART_STACKED: size), the capacity drawn as a quiet reference line so the fill level is visible; several model_id's are faceted to compare algorithms. x := overrides bin/wave when both are present. NULL [SELECT anofox_plot_assignment(a) FROM (VALUES (1, 'x1', 3, 10), (1, 'x2', 4, 10), (2, 'x3', 6, 10)) a(bin, item, size, capacity)]
anofox_plot_checks macro Data-quality checks from a check table (anofox-tabular run_checks / agg_check as they come; the other checks with their measure as value): one panel per check (own scale), the value as a dot coloured by status (fail red, warn amber, pass green) against the lower_threshold..upper_threshold acceptance band, one lane per identifier, the message on hover. Over several run_ts / ds values (or kind := 'strip') a status strip: one lane per check, a dot per run. x := names the time column. NULL [SELECT anofox_plot_checks(c) FROM (VALUES ('rows', 'pass', 120.0, 100.0, NULL), ('customer present', 'fail', 0.083, NULL, 0.05)) c(check_name, status, value, lower_threshold, upper_threshold)]
anofox_plot_compare macro Compare a numeric column across groups with significance brackets (ggpubr's stat_pvalue_manual): pass the raw rows, value := '', factor := '' and tests := a LIST of test STRUCTs — a subquery (SELECT list(t) FROM tukey_hsd_by(...) t WHERE ...), or the aggregate itself (tukey_hsd_agg(y, g), pairwise_t_test_agg, dunn_test_agg; a two-sample STRUCT such as t_test_agg(y, g) for two groups). kind: box (default), violin or points (jitter + group means). Brackets are labelled '{p.format} {p.signif}' from p_adj (else p_value); label := '{p.signif}' / 'p = {p_adj}' changes it, hide_ns drops non-significant pairs (default: above 3 groups); label := 'stars' | 'p' | a template. NULL [SELECT anofox_plot_compare(r, value := 'y', factor := 'g', tests := [{'group1': 'a', 'group2': 'b', 'p_adj': 0.01}]) FROM (VALUES ('a', 1.0), ('a', 1.4), ('b', 2.1), ('b', 2.6)) r(g, y)]
anofox_plot_curve macro Plot a curve table; the geom follows curve_type: roc/calibration (line + diagonal), pr/pdp/lift/lowess (line), km (step), acf/pacf (lollipops + bounds from y_low/y_high), lambda_cv (point ± y_low..y_high on a log x), qq (points + identity). One curve_type per call (GROUP BY curve_type); several model_id/series are coloured. NULL [SELECT anofox_plot_curve(r) FROM (VALUES ('roc', 0.0, 0.0), ('roc', 0.2, 0.7), ('roc', 1.0, 1.0)) r(curve_type, x, y)]
anofox_plot_cv macro Cross-validation layout of a fold table (provisional schema): one lane per fold_id, the train and test windows as bars along ds (or an integer period), so the number of folds, where each training window ends and whether the test windows overlap or leave gaps (said under the plot) show at a glance. Reads ts_cv_folds_by / ts_cv_split_by (id, ds, fold_id, split) and ts_cv_split_folds_by (fold_id, train_start, train_end, test_start, test_end) as they come. Series with identical folds are drawn once; different folds get a panel per id (up to 6). NULL [SELECT anofox_plot_cv(f) FROM (SELECT 1 AS fold_id, 'train' AS split, i AS period FROM range(10) r(i) UNION ALL SELECT 1, 'test', i FROM range(10, 13) r(i)) f]
anofox_plot_decomposition macro Stacked panels of a time-series decomposition sharing one x axis, each with its own y scale: observed, level, trend, each seasonal (seasonal_), remainder (as points). Reads the long `decomposition` table (ds | period, component, value), wide columns (observed, trend, seasonal, remainder; anofox-orchestration's decomposition output) and anofox-forecast's list outputs as they come: ts_mstl_decomposition_by (trend[], seasonal[][], periods[]; x = the period index) and ts_forecast_explain_by (its decomposition STRUCT). One series per call (GROUP BY id). components := ['trend', 'seasonal_7'] picks and orders the panels. Provisional: the long-form column names are to be agreed with anofox-orchestration and anofox-forecast. NULL [SELECT anofox_plot_decomposition(d) FROM (SELECT i AS period, 'trend' AS component, 0.5 * i AS value FROM range(20) r(i) UNION ALL SELECT i, 'seasonal', sin(i) FROM range(20) r(i)) d]
anofox_plot_diagnostics macro The plot.lm 2x2 from an obs (augment) table as one SVG: residuals vs fitted, normal QQ, scale-location and residuals vs leverage (or Cook's distance). NULL [SELECT anofox_plot_diagnostics(o) FROM (VALUES (1, 1.0, 0.1), (2, 2.0, -0.3), (3, 3.0, 0.2), (4, 4.0, 0.0)) o(row_id, fitted, residual)]
anofox_plot_diagram macro A pipeline, lineage or process diagram from a graph table (one row per node: id, title, mainstat, status, group, lane, story, x, y, sparkline, seq) and its edges := a LIST of edge STRUCTs (source, target, value, label, style, status) — a subquery (SELECT list(e) FROM edges e). Grafana node-graph names; producer spellings are read too (stage / label / from_stage / to_stage / count, …), or name the columns: id :=, mainstat :=, source :=, target :=, value :=. format := 'compact' (default) | 'money' | 'percent' | 'metric' for mainstat; direction := 'LR' | 'TB'; style := the edges' default ('flow', 'flow-volume', …). Edges are as wide as their value and coloured by status; loops run back. NULL [SELECT anofox_plot_diagram(n, edges := [{'source': 'a', 'target': 'b', 'value': 3}]) FROM (VALUES ('a', 'Load', 10), ('b', 'Clean', 8)) n(id, title, mainstat)]
anofox_plot_distribution macro A categorical distribution from a distribution table (anofox-decide's decide_choice_distribution MAP / decide_table's distribution column, reached with SELECT t.question_id AS id, u.e.key AS option, u.e.value AS probability FROM t, unnest(map_entries(t.distribution)) AS u(e); question_id and model are also read as id and model_id): one row per option (id, option, probability, model_id optional). One id draws a plain bar per option; several ids draw a 100% stacked bar per id (CATEGORY: option). Several model_id's are faceted to compare models. NULL [SELECT anofox_plot_distribution(d) FROM (VALUES ('q1', 'yes', 0.7), ('q1', 'no', 0.3)) d(id, option, probability)]
anofox_plot_events macro Timeline of an events table (alarms, parameter changes, calibration proposals): a dot per event at its ds (or integer period) in one lane per series (else per kind), coloured by severity (alarm red, warning amber), with kind, label, value and threshold on hover. To draw the events over a forecast, pass them to anofox_plot_prediction(p, events := (SELECT list(e) FROM alarms e)). NULL [SELECT anofox_plot_events(e) FROM (VALUES (3, 'stockout', 'alarm', 'on hand 0'), (7, 'drift', 'warning', 'CUSUM 4.2')) e(period, kind, severity, label)]
anofox_plot_matrix macro Heatmap of a matrix table (row, col, value; anofox-tabular profile_correlations as it comes: column_a x column_b, pearson): one tile per cell, a diverging scale around 0, -1..1 for correlations (domain := [lo, hi] overrides), a pair list mirrored to the full matrix with a diagonal of 1 (mirror := false keeps it as given). Cell values are written when there are at most 144 cells (labels := overrides). value := picks another measure (value := 'spearman'). Confusion and setup matrices (row, col already the full grid) are drawn the same way, without mirroring. NULL [SELECT anofox_plot_matrix(m) FROM (VALUES ('a', 'b', 0.4), ('a', 'c', -0.2), ('b', 'c', 0.7)) m(row, col, value)]
anofox_plot_obs macro One regression diagnostic panel from an obs (augment) table. kind: resid_fitted (residual vs fitted + loess), qq (normal QQ of std_residual), scale_location (sqrt|std_residual| vs fitted), leverage (std_residual vs leverage, 2p/n line when n_params is set) or cooks (Cook's distance by row_id with the 4/n line). The label_top rows with the largest cooks_d are labelled by row_id. NULL [SELECT anofox_plot_obs(o, kind := 'qq') FROM (VALUES (1, 1.0, 0.1), (2, 2.0, -0.3), (3, 3.0, 0.2)) o(row_id, fitted, residual)]
anofox_plot_partial macro Partial-residual plots of a _partial_residuals_by table, one panel per term. kind := 'component' (component + residual: partial_residual over x, the term's component line and a loess) or 'added_variable' (y_residual over x_residual with the least-squares line through the origin, whose slope is the term's coefficient). NULL [SELECT anofox_plot_partial(p) FROM (VALUES ('x1', 1.0, 0.4, 0.5), ('x1', 2.0, 1.1, 1.0), ('x1', 3.0, 1.4, 1.5)) p(term, x, partial_residual, component)]
anofox_plot_prediction macro Fit / forecast plot of a prediction table: observed y as points, the yhat line and the yhat_lower..yhat_upper band over x (or ds, or an integer period), a nested fan when there are several levels, split boundaries as labelled vertical lines (train/test/future; derived from is_training or a missing y), or by model_id/model_name when there are several models. x := / y := name other columns (e.g. x := 'x1' for a *_fit_predict_by output); several id series become panels (facet := overrides). events := (SELECT list(e) FROM alarms e) draws an events table (ds | period, kind, label, severity) as vertical lines coloured by severity. statsforecast's wide -lo- / -hi- columns are read as one row per model and level. NULL [SELECT anofox_plot_prediction(p) FROM (VALUES (1, 1.0, 1.1, 0.9, 1.3), (2, 2.0, 1.9, 1.6, 2.2), (3, NULL, 3.1, 2.6, 3.6)) p(x, y, yhat, yhat_lower, yhat_upper)]
anofox_plot_schedule macro Gantt chart of a schedule table: one lane per resource, a bar per task from start to end, coloured by status (on time/late, else muted), a late task's connector to its due date, faceted by model_id (algorithm) when there are several (up to 6; else GROUP BY model_id or WHERE model_id = …). Columns: resource, task, start, end, model_id, due, status — one row per task. Replaces drawing a schedule as a scatter with xmin/xmax error bars. NULL [SELECT anofox_plot_schedule(s) FROM (VALUES ('L1', 'O1', 0, 10, 'on time'), ('L1', 'O2', 10, 25, 'late'), ('L2', 'O3', 0, 15, 'on time')) s(resource, task, start, "end", status)]
anofox_plot_summary macro Model comparison from a summary (glance) table: a grid of small panels, one per metric with its own scale, the models (model_id, or model_id · id for several groups) on the vertical axis, the value as a dot (± conf_low..conf_high) with its number beside it and the best model highlighted for R², AIC, BIC, RMSE, …. Bookkeeping metrics (n_observations, n_features, iterations) are left out unless metrics := ['r_squared', 'n_observations', …] picks the panels. NULL [SELECT anofox_plot_summary(s) FROM (VALUES ('ols', 'r_squared', 0.81), ('ridge', 'r_squared', 0.79), ('ols', 'aic', 72.4), ('ridge', 'aic', 75.0)) s(model_id, metric, value)]
anofox_plot_terms macro Coefficient plot of a terms (tidy) table, aggregated over its rows: pass the row, SELECT anofox_plot_terms(c) FROM coefs c. Columns: term, estimate, conf_low, conf_high (optional), model_id (several models are dodged and coloured). With index_value (+ index_name) it draws each term's estimate and band over the index instead: a λ path in one panel with direct labels (highlight := […], lambda_min := / lambda_1se := mark λ.min / λ.1se, facet := true for a panel per term), a LARS path on the |β|₁ fraction, the quantile process and rolling coefficients (window_end dates) per term with a full-sample reference (reference := a terms LIST). title := / x_label := / y_label := label the chart (no inner title by default). NULL [SELECT anofox_plot_terms(c) FROM (VALUES ('x1', 0.5, 0.2, 0.8), ('x2', -0.3, -0.7, 0.1)) c(term, estimate, conf_low, conf_high)]
anofox_plot_tests macro Comparison forest of a test table (tukey_hsd_by, pairwise_t_test_by, dunn_test_by, or an unnested *_test_agg STRUCT): one row per test (test_id, else 'group1 vs group2', else the group / method), the estimate with conf_low..conf_high when the test has one, else the statistic, else -log10(p), coloured by significance at alpha, the (adjusted) p-value and stars beside each row. Several groups (id, model_id or the GROUP BY column of an unnested aggregate) get one panel each. Several methods (ADF and KPSS) get one panel per method (own x, groups on a shared y) unless the axis is -log10(p). value := 'estimate' | 'statistic' | 'p' overrides the measure. A column holding the aggregate's STRUCT or LIST is expanded: SELECT anofox_plot_tests(s) FROM (SELECT tukey_hsd_agg(y, g) AS r FROM t) s. NULL [SELECT anofox_plot_tests(t) FROM (VALUES ('b-a', 'a', 'b', 1.2, 0.4, 2.0, 0.003), ('c-a', 'a', 'c', 0.3, -0.5, 1.1, 0.41)) t(test_id, group1, group2, estimate, conf_low, conf_high, p_adj)]
anofox_render scalar Render a chart specification to an SVG string. The spec is JSON carrying 'rows' (the data), 'roles' (which column plays which part: XAXIS, BARCHART, CATEGORY, …) and optional 'width' and 'height' – or 'plot' (terms, prediction, curve, summary, obs, diagnostics, tests, compare, partial, auto) with rows named by the anofox contract. The anofox_bar/xy/… and anofox_plot* macros build this spec for you. NULL [anofox_render(json_object('rows', to_json([{c0: 'Jan', c1: 10}]), 'roles', '[[0,"XAXIS"],[1,"BARCHART"]]'::JSON))]
anofox_scatter macro Render a scatter plot of two columns as SVG. Shorthand for anofox_xy(x, y, kind := 'SCATTER'). NULL [SELECT anofox_scatter(x, y) FROM (VALUES (1.5, 10), (2.5, 20)) t(x, y)]
anofox_xy macro Aggregate two columns into a single-series chart and render it as SVG. 'kind' selects the mark (BARCHART, LINECHART, SCATTER, AREACHART); x becomes the axis and y the value. NULL [SELECT anofox_xy(x, y, kind := 'LINECHART') FROM (VALUES ('Jan', 10), ('Feb', 20)) t(x, y)]
anofox_xyc macro Aggregate three columns into a multi-series chart and render it as SVG, with 'series' splitting the data into categories. NULL [SELECT anofox_xyc(x, y, s) FROM (VALUES ('Jan', 10, 'EU'), ('Jan', 8, 'US')) t(x, y, s)]

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
datazoo_banner Show the DataZoo feedback banner when an extension is loaded in an interactive terminal (at most once a day per extension). BOOLEAN GLOBAL [] true
temp_directory Set the directory to which temporary files are written. VARCHAR GLOBAL [] post.db.tmp