spacr.qt.widgets.trellis_spec¶
Small multiples — one chart repeated per group, on axes that really are shared.
A trellis is the cheapest correct comparison there is: the same plot, once per plate or once per gene, laid out in a grid so the eye does the differencing. It is also the easiest one to break, in a way that produces a figure everybody believes. Three rules, and this module exists to hold all three in one place.
Shared axes, by default and loudly. Two panels whose y axes differ by an
order of magnitude look identical, so a per-panel autoscale turns “the knockdown
halved the count” into “the two panels look the same”. Every scale here is
computed over the whole grid unless the user explicitly asks otherwise, and
when they do ask otherwise Trellis.notice says so in words — because a
free-scale trellis screenshotted into a slide has no other way to admit it.
SCALE_ROW and SCALE_COL are the middle grounds: panels down a
column of the grid share, or panels across a row do, which is what you want when
the rows are three different measurements and the columns are three conditions.
Every panel prints its n. Not a toggle. A box over four objects and a box
over four thousand are the same box, and the number that separates them is the
one an option would let people turn off. The same stance
spacr.qt.widgets.pivot_spec takes about cells, and LOW_N is
imported from there rather than written again.
Empty panels are drawn. Inherited whole from
spacr.qt.widgets.graph_spec.facet_grid(): the grid is the full cartesian
product of the two facet channels, so “plate 3 / row H was measured and nothing
survived the filter” and “there is no plate 3 / row H” stay different pictures.
What this adds over the Graph Builder¶
The Graph Builder already facets — it has to, since dragging a column onto Facet ↓ has to do something. What is here and not there:
two-way faceting as the subject rather than a decoration, including
wrap()for the single-channel case, so twelve plates are a 4 × 3 block rather than a strip twelve panels wide;per-panel scale groups — the four
SCALE_MODESabove, per axis, where the Graph Builder has one shared/not-shared boolean per axis;per-panel n, and a summary of the spread of n across the grid, because the first question about any trellis is whether the small panels are small because of biology or because of sampling.
Colour and size are never per-panel¶
Whatever the scale mode, the colour levels, the colour ramp limits and the size limits are computed once over the whole grid. A gene that is blue in one panel and orange in the next is not a legend, it is a trap; and a mark area that means 200 px² on the left and 20 000 px² on the right is worse, because nothing on screen says so.
No Qt in here — pure pandas and numpy, like the modules it builds on.
Classes¶
A computed grid: the panels, their scales, and what to say about it. |
|
One panel: which group it holds, which rows, and its own scales. |
|
A |
Functions¶
|
Lay |
|
Grid positions for |
Module Contents¶
- class spacr.qt.widgets.trellis_spec.Trellis[source]¶
A computed grid: the panels, their scales, and what to say about it.
- Parameters:
spec – the trellis specification the grid was computed from.
frame – the rows that were laid out — post-filter, post-large-data policy. Panel indices are positions into this.
source – the rows before the large-data policy, for an exact brush.
kinds – column name to column kind for
frame, with the graph spec’s role overrides applied.grid – the facet layout the panels came from.
panels – every panel in row-major order, blank slots included.
shape –
(rows, columns)of the panel grid.data – the large-data decision, carried whole so the caller can print
noticeunchanged.shared – the whole-grid scales, computed whatever the modes are, so a caption can say what the shared limits would have been.
- axes_are_comparable() bool[source]¶
Whether a difference between two panels is a difference in the data.
- brush(x0: float, y0: float, x1: float, y1: float, *, row: int = 0, col: int = 0) numpy.ndarray[source]¶
Rows of
sourceinside a rectangle swept on one panel.Evaluated against the unsampled rows and against that panel’s own scales, so a brush stays exact when the panel was drawn from a sample or as a density raster, and a categorical axis under a free scale is matched against the levels that panel actually drew.
- Parameters:
x0 – one horizontal edge of the rectangle, in data units.
y0 – one vertical edge, in data units.
x1 – the other horizontal edge; the edges may come in either order.
y1 – the other vertical edge.
row – the grid row of the panel brushed, from 0.
col – the grid column of the panel brushed, from 0.
- Returns:
a boolean mask over
source; allFalsefor a blank slot.
- low_n_panels() Tuple[TrellisPanel, ...][source]¶
Every panel holding too few rows to read as a distribution.
What lets a view mark them rather than draw a confident-looking chart of four points beside one of four thousand.
- Returns:
the sparse panels.
- n_at(row: int, col: int) int[source]¶
How many rows landed in one grid position.
- Parameters:
row – the grid row, from 0.
col – the grid column, from 0.
- Returns:
the row count.
- panel(row: int, col: int) TrellisPanel[source]¶
The panel at one grid position.
- Parameters:
row – the grid row, from 0.
col – the grid column, from 0.
- Returns:
the panel.
- scales_at(row: int, col: int) spacr.qt.widgets.graph_spec.Scales[source]¶
The axis limits used at one grid position.
- Parameters:
row – the grid row, from 0.
col – the grid column, from 0.
- Returns:
that panel’s scales.
- class spacr.qt.widgets.trellis_spec.TrellisPanel[source]¶
One panel: which group it holds, which rows, and its own scales.
indexholds positional indices into the frame the trellis was computed over, exactly asFacetPaneldoes and for the same reason — a measurement frame carries a duplicated index often enough that positions are the only safe currency.- Parameters:
row – the panel’s grid row, from 0.
col – the panel’s grid column, from 0.
row_level – the row-facet level this panel holds, or
None.col_level – the column-facet level this panel holds, or
None.index – positional indices of this panel’s rows in the laid-out frame.
scales – the axis limits and orders this panel is drawn with.
occupied –
Falsefor the blank slots at the end of a wrapped grid. A blank slot has no group at all, which is different from a group with no rows: the first is “the grid is 3 wide and 7 does not divide by 3”, the second is a fact about the data. The renderer hides the first and draws the second.
- frame(source: pandas.DataFrame) pandas.DataFrame[source]¶
The rows this panel holds, taken out of
source.POSITIONAL, NOT LABELLED.
indexholds positions into the frame the trellis was computed over, so this must be given THAT frame – a reindexed or differently filtered one would select the wrong rows without raising.- Parameters:
source – the frame the trellis was computed over.
- Returns:
just this panel’s rows.
- class spacr.qt.widgets.trellis_spec.TrellisSpec[source]¶
A
GraphSpecplus the grid’s rules.Composition rather than a subclass: the graph spec is a complete, serialisable description of one chart, and a trellis is that chart and a layout. Keeping them apart means a trellis can hand its inner spec to the Graph Builder, the gate editor or the feature explorer unchanged, and a chart built anywhere can be dropped into a grid without conversion.
- Parameters:
graph – what to draw in each panel — the six channels, the kind, the bins, the point budget. Its
facet_row/facet_colare the grid.scale_x – one of
SCALE_MODESfor the horizontal axis.scale_y – likewise for the vertical one.
wrap – for a grid faceted on one channel only, lay the levels out this many panels wide instead of in a single strip.
0keeps the strip. Ignored with a notice when both facet channels are in use — wrapping a two-way grid would put unrelated levels in the same row.
- Raises:
SpecError – on an unknown scale mode or a wrap beyond
MAX_WRAP, at the point the spec is built.
- __post_init__() None[source]¶
Coerce the inner graph spec and validate the scales and the wrap.
- Raises:
SpecError – if either scale mode is not one this module offers, or if
wrapis negative or wider than the maximum –0means no wrapping.
- describe(kinds: Mapping[str, str] | None = None) str[source]¶
The whole grid in one line: the chart, then anything non-default.
SAYS ONLY WHAT DIFFERS. A description that always listed the scale policies would bury the chart it is describing under two clauses that are usually “shared”.
- Parameters:
kinds – display names for chart kinds, when the caller has them.
- Returns:
a one-line description.
- classmethod from_dict(payload: Mapping[str, Any]) TrellisSpec[source]¶
Rebuild a spec from plain data.
UNKNOWN KEYS ARE IGNORED rather than raising, so a spec saved by a later version still opens here with the parts this version knows.
- Parameters:
payload – what
to_dict()produced.- Returns:
the rebuilt spec.
- classmethod from_json(text: str) TrellisSpec[source]¶
Rebuild a spec from JSON text.
- Parameters:
text – the JSON text.
- Returns:
the rebuilt spec.
- to_dict() Dict[str, Any][source]¶
This spec as plain data, graph included.
- Returns:
a JSON-safe dict.
- to_json() str[source]¶
This spec as JSON text, with keys sorted so the file is diffable.
- Returns:
the JSON text.
- with_channel(channel: str, column: str | None) TrellisSpec[source]¶
A copy with one of the graph’s channels rebound.
- Parameters:
channel – the channel’s name, such as
xorcolour.column – the column to bind, or None to clear it.
- Returns:
the new spec.
- with_graph(graph: spacr.qt.widgets.graph_spec.GraphSpec) TrellisSpec[source]¶
A copy wrapping a different graph spec.
A COPY: a spec is a value, so the one a view is already drawing from is never edited underneath it.
- Parameters:
graph – the replacement graph spec.
- Returns:
the new spec.
- with_kind(kind: str | None) TrellisSpec[source]¶
A copy drawn as a different chart kind.
- Parameters:
kind – the chart kind, or None to let the data decide.
- Returns:
the new spec.
- with_scales(scale_x: str | None = None, scale_y: str | None = None) TrellisSpec[source]¶
A copy with either axis’s scale policy changed.
An omitted argument KEEPS the current policy rather than clearing it, so changing only the y scale does not silently reset x.
- Parameters:
scale_x – the x policy, or None to keep it.
scale_y – the y policy, or None to keep it.
- Returns:
the new spec.
- with_wrap(wrap: int) TrellisSpec[source]¶
A copy wrapping at a different number of columns.
- Parameters:
wrap – how many panels per row.
- Returns:
the new spec.
- property colour: str | None[source]¶
Forwarded from the wrapped graph spec.
The trellis composes a graph rather than subclassing it, so the channel lives on the graph; this is the spelling that saves every caller writing
spec.graph.colour.- Returns:
the column bound to colour, or None.
- property facet_col: str | None[source]¶
Forwarded from the wrapped graph spec.
The trellis composes a graph rather than subclassing it, so the channel lives on the graph; this is the spelling that saves every caller writing
spec.graph.facet_col.- Returns:
the column bound to the facet columns, or None.
- property facet_row: str | None[source]¶
Forwarded from the wrapped graph spec.
The trellis composes a graph rather than subclassing it, so the channel lives on the graph; this is the spelling that saves every caller writing
spec.graph.facet_row.- Returns:
the column bound to the facet rows, or None.
- property is_faceted: bool[source]¶
Whether this is a grid at all, rather than one chart.
- Returns:
True when either facet channel is bound.
Both axes shared — the state in which the grid is comparable.
- spacr.qt.widgets.trellis_spec.trellis(frame: pandas.DataFrame, spec: TrellisSpec | None = None, *, levels_source: pandas.DataFrame | None = None) Trellis[source]¶
Lay
frameout as small multiples.- Parameters:
frame – the rows to draw — already narrowed by whatever filter the views share.
levels_source – where the facet levels come from when that is not
frame; passed straight through tofacet_grid(), so a level that exists in the population but drew no rows still gets its panel.
- Returns:
a
Trelliswhose panels are the full grid, blanks and empties included, each carrying its ownScalesand its n.
- spacr.qt.widgets.trellis_spec.wrap_positions(count: int, wrap: int) Tuple[Tuple[int, int], ...][source]¶
Grid positions for
countlevels laid outwrappanels wide.Row-major, so reading order and level order are the same order. Returned rather than applied, so the caller can also work out how many trailing slots are blank — a wrapped grid of seven levels at three wide has two blanks, and they are placeholders, not empty groups.
- Parameters:
count – number of levels to place.
wrap – panels per row; below 1 puts every level in its own row of one.