Skip to content

Scatter Plots

A scatter series stores a point cloud per step, not a number. Unlike histograms there is no reduced reading: scatter series define no reductions, so fetch_scatter_series is the only endpoint serving them.

frame = client.fetch_scatter_series("run-001", "latents")

The frame is long — index (run, series, step), one row per step, each cell holding that step’s whole coordinate array:

xs ys
run series step
run-001 latents 0 [0.12, -1.4, ...] [0.55, 0.21, ...]
100 [0.08, -0.9, ...] [0.61, 0.18, ...]

xs and ys are float64 arrays of equal length. A 3D series adds a zs column; on a fetch that matches both kinds, 2D rows carry None there, so the two stay structurally distinct. One fetch serves both scatter kinds by default — pass kind= to narrow:

from metrana.query import SeriesValueKind
client.fetch_scatter_series("run-001", "trajectory", kind=SeriesValueKind.SCATTER_3D)

The titles the run logged come back in frame.attrs["axis_titles"], keyed by (run, series) — a dict with "x"/"y"/"z" entries for the titles that exist (a title never logged is simply absent):

titles = frame.attrs["axis_titles"].get(("run-001", "latents"), {})
ax.set_xlabel(titles.get("x", ""))

They are also on SeriesDefinition.metadata_labels (under the xt/yt/zt keys) when you go through list_series_definitions.

include_extents=True attaches each series’ extents as frame.attrs["extents"], keyed by (run, series). Each value is a ScatterSeriesExtent:

Field Meaning
first_step Lowest step the series has, ignoring the steps selection.
last_step Highest step the series has, ignoring the steps selection.
points_count Upper bound on the number of steps — counted before deduplication, so re-logged steps inflate it.
data_points_count Upper bound on the total number of individual points across all steps — a sizing hint for range fetches.

As for histograms, the extent is independent of the steps you asked for: fetch just the last point set and you still learn the series’ full range, enough to render a step slider without a second scan.

from metrana.query import LastSteps
frame = client.fetch_scatter_series("run-001", "latents", steps=LastSteps(1),
include_extents=True)
extent = frame.attrs["extents"][("run-001", "latents")]
slider_range = (extent.first_step, extent.last_step)

There is no scatter counterpart of reduce_histogram — no single float meaningfully summarises a point cloud, so scatter series never appear on the float endpoints, and the summary listings return only their step skeleton. A selector carrying a reducer is rejected at the call.