Skip to content

Plots reference (mapi-plots)

mapi-plots has two parts. Each part has different dependencies:

Install Modules Imports Runs in
mapi-plots mapi_plots.sanitize, mapi_plots.templates mapi-spec, jsonschema (JSON only) API server, runner, notebooks
mapi-plots[recipes] mapi_plots.convert, mapi_plots.recipes, mapi_plots.packs, mapi_plots.examples + altair, plotly, numpy, pandas, pyarrow, xarray runner images, notebooks

An import-linter contract makes sure that the sanitizer and the templates do not import numpy, pandas, xarray, pyarrow, altair, plotly or mapi_sdk. import mapi_plots loads no large modules, because the exports are lazy.

The PlotSpec envelope

The contract package mapi_spec.plots defines the envelope:

id: core.ppc_fan                    # [A-Za-z0-9_.-]{1,128}
title: Fit and forecast
kind: vega-lite                     # or plotly
spec: {...}                         # Vega-Lite JSON, or Plotly {data, layout, frames, config}
datasets:                           # referenced BY NAME only
  fan: {kind: inline_arrow, data: <base64 Arrow IPC stream>, rows: 84}
  big: {kind: data_ref, path: ui/plotdata/fan.arrow, format: arrow}
bindings: {x: "dim:time", y: "role:target", facet: "dim:geo"}   # templates only
schema_version: v6.4.1
persona: both                       # guided | expert | both
caption_plain: "The line is the model's median estimate ..."
provenance: {recipe: core.ppc_fan@1.0.0, bundle_id: ..., generated_at: ...}
  • Vega-Lite references data with {"data": {"name": "fan"}}.
  • Plotly references a column with {"$ref": "<dataset>.<column>"} in a data-array attribute. It also uses this reference in arrayOk attributes, for example marker.color. The part before the first . is the dataset. The remaining part is the column (trace0.marker.color → dataset trace0, column marker.color). The JSON Schema of the reference is mapi_plots.sanitize.PLOTLY_REF_SCHEMA. It is also in the file mapi_plots/sanitize/schemas/plotly-ref.schema.json:
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:mapi:schema:plotly-dataset-ref:v1",
  "type": "object",
  "properties": {"$ref": {"type": "string",
    "pattern": "^[A-Za-z_][A-Za-z0-9_\\-]{0,63}\\.[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$"}},
  "required": ["$ref"],
  "additionalProperties": false
}
  • The maximum size of each inline Arrow dataset is 2 MiB of base64. Recipes write datasets in a compact format (float32, int32, dictionary-encoded strings). Timestamps are UTC milliseconds.

Sanitizer

from mapi_plots.sanitize import sanitize_plot, check_plot, sanitize_spec, SanitizeLimits

result = sanitize_plot(doc)            # PlotSpec | dict | JSON str | bytes
result.plot                            # validated, sanitized PlotSpec
result.changes                         # list[Issue] (severity info): HTML stripped, $schema pinned ...

issues = check_plot(doc)               # errors + changes, never raises
spec, changes = sanitize_spec("plotly", figure_dict)   # bare spec, no envelope

SanitizeError is a MapiError. err.issues lists all violations, up to max_issues. Each violation has these items:

  • An RFC 6901 pointer.
  • A message that the platform wrote.
  • The rule, in context["rule"].
  • Text from the user, if there is some. This text is only in untrusted_text.

err.rules is the set of rules that fired. The error code is the most severe code that is present:

Code HTTP Rules
PLOT_UNSAFE 422 url_data, href, external_image, tile_map, proto_key, script_url, css_url, expression, format_type, config
PAYLOAD_TOO_LARGE 413 too_large, too_deep, too_many_nodes, string_too_long, array_too_long
SPEC_PARSE_ERROR 400 parse (JSON that is not valid, duplicate keys, NaN/Infinity)
SCHEMA_INVALID 422 schema, envelope, dataset_ref, typed_array

Changes use the rules html_stripped, schema_url and template_pruned. Changes are not errors.

Limits

SanitizeLimits field Default
max_document_bytes 16 MiB (the full PlotSpec, with the inline Arrow data)
max_spec_bytes 2 MiB (the spec member)
max_depth 48
max_nodes 200 000
max_string_bytes 64 KiB (the envelope sets the limit for inline Arrow payloads)
max_array_items 100 000
max_expr_chars 2 000
max_typed_array_bytes 8 MiB
max_issues 50

Vega-Lite rules

  1. $schema must be an official Vega-Lite v5 or v6 URL, or not present. The sanitizer sets it to v6.4.1.
  2. The spec must be valid against the vendored Vega-Lite v6.4.1 JSON Schema (draft-07, local $refs only).
  3. Do not use these items: a url key at any location, href, and the image mark. formatType can only be number, time or utc. Do not enable config.customFormatTypes.
  4. Expressions must use only Vega functions from the allow list. Expressions are:

    • expr, calculate and filter/test strings, and their and/or/not compositions.
    • Each …Expr key, for example labelExpr.
    • Filters in brackets in selection event streams.

    The sanitizer rejects these items: method calls, this/new, assignment, statements, template and regex literals, and \u/\x escapes. It also rejects non-ASCII characters outside strings, and computed member access with a key that is not a literal. It also rejects browser globals (window, document, fetch, globalThis …) and event.view.

Plotly rules

  1. The top level has only data, layout, frames and config.
  2. The sanitizer does a check of trace types, attribute names and value types against a table. This table comes from the official plotly.js 4.1.1 plot-schema.json. It has enumerations, flag lists, subplot ids, colour scales and info arrays. It also has subplot keys of the xaxis2 type, and template keys of the annotationdefaults type.
  3. Unknown attributes are errors. In layout.template, the sanitizer removes unknown attributes and tile-map sections, and reports them.
  4. Data arrays are inline lists, dataset references or plotly.py typed arrays ({dtype: i1|u1|u1c|i2|u2|i4|u4|f4|f8, bdata, shape?}). The sanitizer does a check of the base64, length and shape of typed arrays.
  5. The sanitizer rejects these items:
    • Tile-map traces and subplots.
    • Image sources that are not data:image/(png|jpeg|gif|webp);base64. This rule also applies in the relayout arguments of updatemenus/sliders.
    • url/href/topojsonURL keys.
    • Network config options.
  6. Strings can keep only <b> <i> <br> <sub> <sup>. The sanitizer removes attributes and escapes a stray <. <extra> stays in hover templates.

Vendored schemas

File Source Regenerate
vega-lite-v6.4.1.json https://vega.github.io/schema/vega-lite/v6.4.1.json (byte-for-byte) scripts/vendor_schemas.py --download
plotly-structure-v4.1.1.json made from plotly.js@v4.1.1/dist/plot-schema.json same
VENDORED.json source URLs and SHA-256 digests (a unit test does a check of them) same

Templates (mapi_plots.templates)

resolve_bindings(bindings, schema, *, optional=()) -> dict[channel, BoundField | list[BoundField]]
apply_template(plot, schema, *, optional=(), title=None) -> PlotSpec
template_issues(plot, schema, *, optional=()) -> list[Issue]
Binding Resolves to Vega-Lite type
dim:<name> a dim of the schema time → temporal, numeric → quantitative, ordered/index → ordinal, other → nominal
role:<role> the one variable with that role (lists for tooltip) from the dtype
var:<name> that variable from the dtype
coord:<name> a coordinate (dim or not) from the dim, or nominal if it is not a dim
stat:<name> a summary statistic column (mean, hdi_90_lower, q5 …) quantitative

render_template(template, data, schema=None, *, group=None, value=None, optional=()) (extra recipes) also makes the dataset from an xr.Dataset or a tree group. The default schema is attrs["mapi_schema"]. If that is not available, the function infers the schema. It gets the roles from the role attribute of each variable.

Recipes (mapi_plots.recipes)

get_recipe(id) -> Recipe                 # bundled recipes (core + packs)
standard_recipes() -> list[Recipe]       # entry point  mapi.plots: core
mapi_plots.packs.mmm.mmm_recipes()       # entry point  mapi.plots: mmm
mapi_plots.packs.ml.ml_recipes()         # entry point  mapi.plots: ml
mapi_plots.packs.sci.sci_recipes()       # entry point  mapi.plots: sci (Plotly)
mapi_plots.packs.net.net_recipes()       # entry point  mapi.plots: net (Plotly)
mapi_plots.packs.causal.causal_recipes() # entry point  mapi.plots: causal (v0.13)
Recipe.render(data, **settings) -> PlotSpec       # also Recipe(data, **settings)
Recipe.catalog_entry() -> PlotCatalogEntry        # id, version, persona, required groups/representation

# what the runner calls for outputs.plots: never raises for a single bad plot
report = render_plots(tree, ["core.ppc_fan", "mmm.response_curves@^1"],
                      registry=None, options={"core.ppc_fan": {"facet": "market"}})
report.plots          # sanitized PlotSpecs
report.issues         # skipped plots: REF_UNRESOLVED / SCHEMA_INVALID / USER_CODE_ERROR
                      # (warnings) and PLOT_UNSAFE (error)
report.to_json()      # plots.json sidecar body

To write your own recipe, use the decorator. The result works with Registry.register and the mapi.plots entry-point group:

import altair as alt
from mapi_plots.recipes import Figure, RecipeSettings, named, recipe


class Settings(RecipeSettings):
    var: str = "y_hat"


@recipe("acme.weekly_total", title="Weekly total", settings=Settings, persona="guided")
def weekly_total(inp, s: Settings) -> Figure:
    ds = inp.require("summary")
    frame = ds[s.var].sel(stat="mean").sum("market").to_dataframe().reset_index()
    chart = alt.Chart(named("weekly")).mark_line().encode(x="date:T", y=f"{s.var}:Q")
    return Figure(chart, {"weekly": frame}, caption=f"Total {s.var} per week.")

A recipe can also return a Plotly figure (plotly.graph_objects.Figure or a {data, layout, frames} dict) in Figure.chart. Recipe.render then sends it through from_plotly with the same title, caption, persona and provenance. The recipe can also give its own DataFrames in Figure.datasets. The traces then refer to their columns with {"$ref": "<dataset>.<column>"}. The function plotly_plot applies the inline-text rules to the string columns of these DataFrames.

Settings shared by every recipe

title (override), width (480), height (180). The recipe rejects unknown settings.

Per-recipe settings

Recipe Settings (defaults)
core.data_overview group, vars, along, facet, max_vars (8), max_levels (12), max_rows (20 000)
core.prior_predictive var, num_draws (50), along, levels ([0.5, 0.9])
core.convergence var_names, max_parameters (40), max_traces (6), max_draws (400), max_elements (2 000), rhat_threshold (1.01), ess_per_chain (100), bins (20), table_rows (15)
core.posterior_forest var_names, hdi_prob (0.94), inner_prob (0.5), point (median), max_rows (60)
core.ppc var, observed_var, num_draws (50), bins (20), levels
core.ppc_fan var, observed_var, along, facet, max_facets (12), levels ([0.5, 0.9]), point (median), split_at, columns (3), max_rows
core.fit_vs_actual, core.residuals_over_time var, observed_var, along, facet, max_facets, point
core.decomposition, mmm.decomposition vars, component (auto / channel), along, hdi_prob (0.9), max_components (20). core.decomposition skips a variable with the attr decomposition: exclude if vars does not name it. mmm.decomposition also has outcome (none: attrs.primary_outcome or the first level of an outcome dim).
core.model_comparison ic (auto: precomputed table, else WAIC), var, model_dim (model)
core.counterfactual var, along, hdi_prob (0.9), bins (30)
core.decision_distribution var, candidate, hdi_prob, higher_is_better (true), max_candidates (12)
core.pair var_names, max_params (5), max_draws (500), bins (20), width (520)
core.ridge var, group (auto | posterior | predictions | posterior_predictive), dim, select, hdi_prob (0.94), max_levels (30), max_draws (4 000), points (100), overlap (1.6), descending (true)
core.rank_matrix var, group, dim, select, descending (true: rank 1 is the largest value), max_items (30), max_draws (4 000), labels (auto | always | never)
core.calibration var, observed_var, kind (auto | continuous | binary), prob (0.95), bins (10), n_sim (1 000), seed (0)
core.state_sequence var, group (auto | hidden_states | regimes | posterior | predictions | posterior_predictive | summary | root), state_dim (auto: state, then regime), along, observed_var, select (none: the first level of each other dim), max_states (10), max_times (2 000), max_draws (4 000), width (640), height (140)
core.roc_pr var, observed_var, positive (the larger target value), max_points (500, 20 to 5 000), classes (v0.14, multiclass: the classes to draw; default the 12 most frequent), width (320), height (280)
core.confusion var, observed_var, positive, threshold (0.5, or auto: the largest TPR − FPR), normalize (none | true), width (260), height (240). Multiclass (v0.14): threshold and positive are not used.
core.loss_curve var, group (auto | fit_history | metrics_history), along (step), smooth (none: a window of n / 50 steps), log_y (none: log when max / min > 50), max_points (2 000), width (560), height (220)
core.loo_pit var (a log_likelihood variable), observed_var, prob (0.95), n_sim (1 000), seed (0), max_draws (2 000), k_threshold (0.7)
core.energy bfmi_threshold (0.3), bins (40)
core.prior_posterior var_names (none: every variable in the two groups), max_vars (12), max_cells (8), points (80, the points of each density), little (0.8, the overlap above which the caption says that the data adds little)
mmm.response_curves var, channel, grid, spend_var (spend), current_var (current_spend), hdi_prob, facet
mmm.marginal_cost the same as response curves, + metric (cost | roi), clip_quantile (0.98)
mmm.budget_plans spend_var, outcome_var, plan, channel, reference, hdi_prob
mmm.prior_beliefs channel (channel), height (26, the height of one channel row)
mmm.roi hdi_prob (0.9), breakeven (1.0; none: no line), channel (channel)
mmm.adstock hdi_prob (0.9), facet (false)
mmm.waterfall hdi_prob (0.9), outcome (none: attrs.primary_outcome or the first level)
mmm.spend_effect hdi_prob (0.9), channel (channel)
mmm.lift_tests hdi_prob (0.9), z (1.96, the width of the error bar of a test in standard errors)
mmm.effectiveness hdi_prob (0.9), only_varying (true)
mmm.mediation hdi_prob (0.9)
mmm.cross_effects hdi_prob (0.9)
ml.feature_importance top (20), feature_dim (feature), source (auto | importance | coefficients)
ml.training_curves (v0.15) keys (none: loss and accuracy or rmse, a maximum of 4 panels), label (split), x (step | epoch), smooth (none), log_y (none), max_points (2 000)
ml.confusion_epochs (v0.15) epochs (none: the first, evenly spaced, the best and the last, a maximum of 6), normalize (true | none), max_classes (20; the others are "(other)")
ml.misclassified (v0.15) max_items (24), columns (6). Plotly image traces with z pixel arrays (no source)
sci.field_map var, group (auto | posterior | predictions | posterior_predictive), y, x, frame, select, stats ([mean, sd]; also median, z, q05, q95, draws), n_draws (3), max_draws (400), max_cells (40 000), max_frames (24), colormap, log, origin, aspect (equal | auto), columns (3), width (760), height (280, one row of panels)
sci.vector_field the same as sci.field_map (no n_draws), + component, stats ([mean, sd]; also q05, q95), arrows (20)
sci.profile_bands var, group, observed_var, along, by, select, levels ([0.5, 0.9]), point (median | mean), max_curves (12), max_draws (1 000), colormap (viridis), log (false), width (640), height (360)
sci.ensemble var, group, observed_var, along, select, n_draws (40), levels ([0.9]), point (median | mean | none), max_draws (2 000), seed (0), log (false), width (640), height (320)
sci.area_map var, group, observed, stats (observed, mean, sd, exceedance; also median), threshold (1 for a non-negative output, else 0), mark_above (0.95), log_scale (false), show_edges (false), select, colormap (viridis), classes (9), max_draws (2 000), max_vertices (40 000), columns (2), width (760), height (340, one row of panels)
sci.phylogeny var, group, tip_dim, observed, interval (0.9), point (mean | median), max_tips (400), max_draws (2 000), labels (up to 80 tips), select, colormap (viridis), width (780), height (0: from the number of tips)
sci.rose var, group, observed, angle_dim, units (radians | degrees | hours | days), period, bins (24), frame, select, interval (0.9), scale (auto | count | share), clockwise, max_draws (1 000), max_frames (24), width (560), height (520)
sci.image_residuals the same as sci.field_map (no stats, n_draws), + observed_var, sigma (a variable or a number), sigma_kind (sd | variance | inverse_variance), columns (2), hist_height (200; 0: no histogram). group auto reads posterior_predictive, predictions, posterior
sci.ppc_image_grid the same as sci.field_map (no stats), + observed_var, n_draws (5, at most 11), shuffle (false), seed (0). group auto reads posterior_predictive, predictions, posterior
sci.swatches var, group, observed_var, channel (rgb), item, row (row), col (col), arrange (auto | list | map), prob, prob_as (border | opacity), select, n_draws (3), max_draws (400), max_items (60, list only), tile (20 px), columns (2), seed (0), width (640)
sci.power_spectrum var, group, observed_var, error_var (default <observed>_sd), theory, along, x_coord, select, levels ([0.5, 0.9]), point (median | mean), max_draws (2 000), log_x, log_y (false), width (640), height (360)
sci.point_pattern points (the point dim), x (x), y (y), sd_x (sd_x), sd_y (sd_y), radius (radius), group (auto | none | a variable), field (auto | none | a variable), field_stat (mean | median | sd), ellipses (auto), level (0.9), observed (true), select, max_points (20 000), max_ellipses (2 000), max_cells (40 000), colormap, origin, marker_size (6), width (640), height (560)
net.graph var, node_dim, positions (auto | none | a variable), threshold (auto), signed (auto), directed (auto), group (auto | none | a variable), truth (auto | none | a variable), panels (both | graph | matrix), select, labels (auto), max_nodes (300), max_edges (2 000), seed (0), width (900), height (440)

Extra core recipes

These recipes are in the core pack (mapi_plots.recipes.extras, classification, training and loo, Vega-Lite):

Recipe What it shows
core.pair A corner plot of up to max_params scalar parameter elements. Below the diagonal: thinned draws of each pair. On the diagonal: a histogram of each parameter. Red points are divergent transitions (sample_stats.diverging).
core.ridge One density for each level of dim, sorted by the mean, with a dot for the mean and a bar for the HDI. Each curve has the same peak height.
core.rank_matrix P(rank = k) for each item, sorted by the expected rank, and the matrix of P(row above column). Input: draws (…, item).
core.calibration Continuous targets: the PIT-ECDF difference with a simultaneous envelope (simulated). Targets with values 0 and 1: a reliability diagram with a binomial band for each bin and a histogram of the predicted probabilities. A point prediction (no draws, values from 0 to 1) uses the reliability diagram. A point prediction of a target that is not 0/1 gives an error, because the PIT check needs draws.
core.state_sequence The observed series (points coloured by the most likely state) above the stacked state probabilities. Input: probabilities (…, state, time) with or without draws, a score summary with a stat dim (the plot uses mean), or integer state draws (chain, draw, time). The default variable is smoothed_prob, state_prob or filtered_prob. Without select, the plot fixes each other dim at its first level and the caption names that level.
core.roc_pr Two panels: the ROC curve with the diagonal of a random guess, and the precision-recall curve with the base rate. The titles give the AUC and the average precision. With draws, the caption gives the 90% interval of the AUC (up to 500 draws). Multiclass (v0.14): one-vs-rest curves in a colour for each class, the macro AUC in the title, and the number of classes that are not drawn in the caption.
core.confusion The 2 × 2 table of the actual class and the predicted class at the threshold, as counts or as the share of each actual class. The caption gives the accuracy, the precision, the recall, the specificity, the F1 score and the threshold. Multiclass (v0.14): a K × K table of the most probable class (a maximum of 30 classes; the other classes are (other)), with the accuracy and the macro F1.
core.loss_curve The raw loss (light) and a rolling mean (dark) over the steps, one line for each level of an extra dim (at most 8). The caption gives the first and the last smoothed value and the change over the last 10% of the steps.
core.loo_pit The LOO-PIT ECDF difference with a simultaneous envelope (when posterior_predictive and observed_data are present), and the PSIS Pareto k of each observation with lines at 0.5 and k_threshold.

The recipes core.calibration, core.roc_pr and core.confusion share one input rule. They look for the prediction in the posterior_predictive, predictions and draws groups (with draws, or one probability for each cell), then in summary (stat mean, point or median). The actual values come from observed_data or observed. The two arrays are aligned on their shared cells, and cells with NaN are left out. For a binary target, the target must have two values. The larger value is the positive class unless you set positive. The predictions must be from 0 to 1. From v0.14, core.roc_pr and core.confusion change to multiclass when the prediction has a class dim (meaning: class_probability, the probabilities of the classes add up to 1) or when the actual target has more than two values. The actual classes can be text or whole numbers (3.0 is the class 3).

Marketing-mix recipes of v0.12

All of these recipes accept draws with a sample dim or with chain and draw dims. A recipe that does not find its group or variable raises RecipeDataError. The runner then skips only that plot.

Recipe Input What it shows
core.energy sample_stats.energy(chain, draw) The centred marginal energy and the energy transitions (two histograms), and the BFMI of each chain, mean(diff(E)²) / var(E). The caption names the chains below bfmi_threshold.
core.prior_posterior variables with chain/draw dims in prior and in posterior A density of the prior and of the posterior for each parameter cell, ranked by |Δmean| / sd(prior). The caption gives the overlap of each panel. An overlap above little (0.8) means that the data adds little.
mmm.roi response_curves: contribution at multiplier 1 divided by current_spend (other dims summed). marginal_cost.marginal_roi_total, else a central difference on the grid around 1 A forest of the ROI of each channel (median and HDI), the mROAS next to it, and a break-even line
mmm.adstock adstock: weight(sample, channel, lag), optional mean_lag The weight of each lag with an HDI band, one line for each channel (or one panel with facet)
mmm.waterfall contributions: channel_contribution and every other variable as a component The total of each component over the fit window as steps to the total result
mmm.spend_effect response_curves: contribution at multiplier 1 for each draw and current_spend The share of spend and the share of effect of each channel side by side
mmm.lift_tests the lift_tests group: observed(test), observed_sd(test), model(chain, draw, test) and the coords channel, estimand, start, end, geo Each test (value ± z standard errors) next to the model estimate (HDI)
mmm.effectiveness effectiveness(sample, channel, time), time_varying(channel) The effect of one unit of spend in each period with an HDI band
mmm.mediation mediation: direct(sample, channel), indirect(sample, channel, mediator) Stacked direct and indirect effects of each channel, with the share that the mediators carry
mmm.cross_effects cross_effects: psi(sample, pair) and the coords source, target, kind A forest of each cross-effect with its kind

Causal pack (causal.*) v0.13

The causal models (causal.impact, causal.synthetic_control, causal.did) write their results to bundle groups that these recipes read. See Causal models. All recipes accept draws with a sample dim or with chain and draw dims. A recipe that does not find its group raises RecipeDataError. The runner then skips only that plot. The recipes send medians and intervals for each period to the sanitizer, not the draws.

Recipe Input Settings What it shows
causal.impact impact: actual, counterfactual, effect, cumulative_effect, post hdi_prob (0.9), unit (none: the sum over the unit dim), panels (series, pointwise, cumulative) Three panels on one time axis: the actual values with the median and HDI band of the values without the change, the effect in each period, and the cumulative effect in the effect window. A rule marks the start (and the end). The caption gives the total effect with its interval, the relative effect and the probability that the effect is more than zero.
causal.pre_fit impact (the cells before the start) hdi_prob (0.9), unit The actual values against the band of the model before the change, with the RMSE, the MAPE and the part of the actual values in the band. The caption flags a part below 80%.
causal.donor_weights donor_weights: weight(sample, unit, donor) hdi_prob (0.9), top (15), unit (none: one panel for each treated unit up to 4, else the first unit) Horizontal bars of the mean weight of each donor with HDI rules, sorted. The other donors are summed as "other donors".
causal.event_study event_study: coef(sample, event_time), att, attrs reference hdi_prob (0.9) A point and an HDI rule for each event time, a zero line and a dashed rule between -1 and 0. The reference period is marked. The caption gives the average effect after the change and tells if the coefficients before the change include zero.

Scientific-model pack (sci.*)

The sci.* recipes make Plotly figures for models with gridded outputs, vector fields, profiles and trajectories. They read draws (chain/draw or sample dims) from posterior, predictions or posterior_predictive. They read the actual values from observed_data.

Recipe What it shows
sci.field_map One heatmap panel for each statistic of a gridded output. A slider and a Play button show one frame at a time.
sci.vector_field The length of a vector as colours, arrows of the mean vector, and the sd of the length.
sci.profile_bands The median and the 50% and 90% bands along along, one curve for each level of by, and the actual values. A slider shows one level with its bands.
sci.ensemble Some draws as thin lines, the 90% band of all draws, the median and the actual values.
sci.area_map A map of areas (polygons) coloured by the observed value, the mean, the sd and the probability that the value is above threshold. A cross marks each area where this probability is above mark_above.
sci.phylogeny A tree of the tips (species) next to a strip with the estimate and its interval for each tip, and the observed values.
sci.rose A polar chart of angles: a rose of the observed angles with the 90% range of simulated data in each sector, or a curve around the circle with its limits.
sci.image_residuals The observed image, the model mean, the residual and the normalised residual (obs - mean) / sqrt(sd² + σ²), with a histogram of the normalised residuals against N(0, 1).
sci.ppc_image_grid The observed image next to n_draws images from the model on one colour scale. With shuffle, the panels are in a random order and the caption names the real one.
sci.swatches Squares in the colours of an sRGB output (…, item, rgb) in [0, 1]: observed, mean and some draws. A map when the item dim has row and col coords.
sci.power_spectrum A binned spectrum (…, bin) against a coord such as ell: median, 50% and 90% bands, a theory curve and the observed values with error bars.
sci.point_pattern One dot for each point (localisation, event) at its mean position, an error ellipse for each point, and an optional image or intensity field below the points.

Data for the map, tree and polar recipes:

  • sci.area_map reads the outlines of the areas from ring_x(area, vertex) and ring_y(area, vertex). Fill the unused vertices with NaN. A NaN between points starts a new ring, for example an island. The first dim is the area dim. The optional adjacency(area, area_) matrix (not zero for neighbours) gives the lines of show_edges. The recipe finds these variables in observed_data, constant_data, a geometry group, the group of the output or the root. It draws the areas as filled shapes on axes with equal scales. It does not use a geographic projection or a base map. If the outlines have too many points, the recipe uses fewer points.
  • The Observed panel of sci.area_map reads observed, else the variable with the name of the output in observed_data. If there is none, the recipe reads the variable that the attr mapi_observed of the output names. A model sets this attr when it keeps the observed value under a different name. For example, sci.areal_bym2 keeps the raw SMR as smr for its output rr.
  • sci.phylogeny reads the tree from parent(node): the position of the parent node, or -1 for the root. The optional variables are branch_length(node) (default 1), is_tip(node) (default: the nodes that are not a parent) and tip_label(node). The tips match the levels of the output by label. If there are no labels, tip k (in node order) is level k.
  • sci.rose reads angles in units. One turn is 2π radians, 360 degrees, 24 hours or 365.25 days, or period. If the output has an angle dim (angle_dim, or a dim called angle, direction, hour and so on), the recipe draws a curve. Otherwise it counts the angles in bins sectors.

Notes on the image and spectrum recipes (mapi_plots.packs.sci_images):

  • sci.image_residuals and sci.ppc_image_grid need the observed image in observed_data, with the grid dims of the output. The recipe uses the variable with the same name, the observed_var setting or the only observed variable.
  • Draws from posterior_predictive already hold the noise of the data, so sci.image_residuals adds no σ to them. For a noise-free output (posterior or predictions), it uses sigma or a variable named sigma.
  • With log, sci.image_residuals shows the observed, mean and residual panels as log10. It computes the normalised residual and its histogram in the data's units, because σ is in those units.
  • sci.swatches writes the colours as #rrggbb text in the dataset. The plot sandbox does not change these colours in dark mode, because they are data.
  • Colours that a recipe takes from a colour scale (the groups of net.graph and sci.point_pattern, the classes of sci.area_map, the curves of sci.profile_bands) are also data. They do not change in dark mode, as a colour scale does not. All other colours come from the palette and have a dark variant.

Statistics of sci.field_map:

  • mean, median: the average value and the middle value of the draws.
  • sd: the uncertainty. A high sd shows where the data do not fix the value.
  • z: the mean divided by the sd. Values far from zero are clear.
  • q05, q95: the limits of the 90% interval.
  • draws: n_draws possible fields from the model.

The grid dims come from the settings. If you do not set them, the recipe uses the grid of the output (OutputDecl.grid). It reads the grid from the grid attribute of the variable or from the model manifest in the bundle. The colormap, log and origin settings also come from the grid when you do not set them. If the output has more dims than the plot uses, use select to pick one level of each, for example select: {species: 0}.

The size of a field plot has limits:

  • All the cells of a plot are in one dataset: x, y and one column for each statistic and frame. The recipe keeps this dataset below the inline Arrow limit (2 MB).
  • If the field has too many cells, the recipe calculates the average of each block of k × k cells. A cell with no value (NaN) stays empty. The caption tells the block size.
  • A field of 189 × 193 cells with six panels and one frame has its full resolution.
  • max_frames sets the maximum number of frames. The slider shows frames at equal intervals.

sci.point_pattern reads the variables x and y with one value for each level of the point dim. Draws give the posterior mean and an ellipse from the 2 × 2 covariance of the draws. A score summary gives its point statistic and an ellipse from its sd. Plain values use the sd_x and sd_y variables (or a radius variable, drawn as a circle) when they exist. The ellipse holds the position with probability level. The field is a gridded output (by default the first output with a grid), drawn below the points in the same units.

Network pack (net.*)

Recipe What it shows
net.graph A node-link drawing of the links above threshold (line width shows the value, blue and red show the sign, arrows show the direction) and a heatmap of the full matrix. Dotted lines and open squares show the known or observed links.

net.graph reads an edge variable with two node dims: the second dim has the name of the first plus _, for example (node, node_) or (var, var_). The values can be draws (the recipe uses the posterior mean), a score summary (its point statistic) or plain values. They can be probabilities, weights or partial correlations.

  • positions: a variable (node, latent_dim), for example the latent positions of a latent space model. The recipe uses the posterior mean of the first two coordinates. Without it, the recipe calculates a layout from the mean weights (a spectral start and seeded Fruchterman–Reingold steps). The layout puts strongly linked nodes near each other.
  • group: a variable with one value for each node (by default node_group or group). Categories get one colour each and a legend. Continuous values get a colour scale.
  • truth: a known or observed adjacency (node, node_). By default the recipe uses one from observed_data or constant_data.
  • threshold: by default 0.5 for values in [0, 1], else 10% of the largest absolute value. With draws, the tooltip at the middle of a link shows the share of draws above the threshold.

The plot sandbox shows the Plotly frames, sliders and updatemenus of these plots. In dark mode, it changes the palette colours of the traces to their dark variants, the same as for Vega-Lite. It also applies the theme to each polar subplot (barpolar, scatterpolar), for example in sci.rose.

The convergence diagnostics are rank-normalised split R-hat and bulk/tail ESS (Vehtari et al. 2021). The package calculates them with numpy. Tests make sure that the values agree exactly with ArviZ.