Card 1619. LatticeChart draws a chart viewer of a grid's dataset. It is bound
to the grid's own dataset through the grid's own binding, so it follows the grid's
filters, quick search and selection live in the browser: filtering the grid
sends nothing to Python and the chart redraws by itself.
from ipywidgets import HBox
from lattice_grid_jupyter import LatticeGridWidget, LatticeChart
grid = LatticeGridWidget(df)
chart = LatticeChart(grid, type="bar", x="segment", y="revenue")
HBox([grid, chart]) # side by side; VBox stacks them- Display the grid. The chart borrows the grid the browser has rendered, so the grid widget must be displayed (in the same box, or in any cell of the same page). Until it is, the chart says so; it then draws without being re-run.
- A DataFrame directly:
LatticeChart(df, type="scatter", x="a", y="b"). The frame travels once, as columns, to a hidden grid the chart binds to.chart.set_data(new_df)redraws. A frame of 100,000 rows or more is refused by name: show it in a (windowed)LatticeGridWidgetand bind the chart to that. - The spec is plain data. Everything after
typeis the chart spec.snake_casekeys map to the grid's camelCase (empty_text->emptyText); nested dicts and lists pass as they are; functions raiseTypeError. A key the chart does not recognise is reported by name as aLatticeGridWarning([lattice] config.unknown:scor: ..., the grid's own warning id) and left out. - Change it live:
chart.update(type="roc", label="churned", score="p")(the spec you give replaces the old one), orchart.type = .../chart.spec = {...}. - Loads only what it uses. The base chart module plus the one module the type
needs (
roc->chart-roc): about 440 KB and 5 KB, never one bundle with every chart. Withoffline=Trueon the grid they travel in the widget like the grid bundle; otherwise they come from jsDelivr, pinned to the grid version. chart.drawsis how often the browser has drawn the chart, so a notebook can see a filter redraw it.
A windowed grid keeps its rows in Python and the browser holds one window. A chart over it never reads rows:
bar,horizontalBar,line,step,area,pie,donut,funnelandwaterfallwithxandyare drawn from the grouped aggregates Python already answers for the grid (the same request the grid's own subtotals use), over the whole frame and under the grid's filters. A filter re-aggregates; scrolling does not.yis summed unlessmeasures=[{"col": "y", "fn": "avg"}]says otherwise.- every other type needs the rows themselves, so it is refused with a named
warning (
chart.windowed.refused:<type>), the chart shows the reason, and nothing is read.
The fixture is seeded and synthetic; every block runs as written.
import numpy as np, pandas as pd
from ipywidgets import HBox, VBox
from lattice_grid_jupyter import LatticeGridWidget, LatticeChart
rng = np.random.default_rng(0)
n = 240
t = np.arange(n)
churned = rng.integers(0, 2, n)
df = pd.DataFrame({
"day": pd.date_range("2024-01-01", periods=n, freq="D").strftime("%Y-%m-%d"),
"segment": rng.choice(["alpha", "beta", "gamma"], n),
"revenue": rng.normal(100, 20, n),
"visits": rng.normal(50, 10, n),
"tenure": rng.normal(24, 6, n),
"sales": 100 + t * 0.5 + 10 * np.sin(t / 7) + rng.normal(0, 3, n),
"forecast": 100 + t * 0.5 + 10 * np.sin(t / 7),
"low": 90 + t * 0.5,
"high": 110 + t * 0.5 + t * 0.05,
"trend": 100 + t * 0.5,
"season": 10 * np.sin(t / 7),
"resid": rng.normal(0, 3, n),
"churned": churned,
"score": np.clip(churned * 0.3 + rng.random(n) * 0.7, 0, 1),
})
grid = LatticeGridWidget(df, offline=True)Bar (a sum per category; the grid's filter changes it):
bar = LatticeChart(grid, type="bar", x="segment", y="revenue", title="Revenue by segment")
HBox([grid, bar])Line:
line = LatticeChart(grid, type="line", x="day", y="sales")Pie:
pie = LatticeChart(grid, type="pie", x="segment", y="revenue")Scatter (selection=True dims every point except the rows selected in the grid):
scatter = LatticeChart(grid, type="scatter", x="visits", y="revenue", selection=True)ROC (also curve="pr" for precision-recall and curve="calibration"; label is the
outcome column, positive= names the positive value):
roc = LatticeChart(grid, type="roc", label="churned", score="score")Fan (history, forecast and an interval that widens):
fan = LatticeChart(grid, type="fan", x="day", y="sales", forecast="forecast", lower="low", upper="high")Decomposition (observed, trend, seasonal and residual panels on one x axis):
decomposition = LatticeChart(grid, type="decomposition", x="day", observed="sales",
trend="trend", seasonal="season", residual="resid")SPLOM (every pair of 2 to 6 numeric columns):
splom = LatticeChart(grid, type="splom", columns=["revenue", "visits", "tenure"])Hexbin (a scatter that reads as density at scale; radius= is the hexagon size in pixels):
hexbin = LatticeChart(grid, type="hexbin", x="visits", y="revenue", radius=14)Ridgeline (one density ridge per category):
ridgeline = LatticeChart(grid, type="ridgeline", x="segment", y="revenue")Layout. Charts and grids are ordinary widgets:
VBox([grid, HBox([roc, fan])])