spacr.qt.widgets.annotation_umap_tab

Display held-out UMAP quality checks for annotated cells.

The panel delegates computation to spacr.annotation_umap_qc. It tunes the embedding on one control subset and evaluates separation on held-out controls, reports neighbour purity rather than cluster membership, and refuses circular evaluation when phenotype scores were used to select the annotated cells.

THE EMBEDDING IS DRAWN THROUGH THE SAME PLOT EVERY OTHER PANEL USES – spacr.qt.widgets.fast_plots.FastPlot beside ResultsTable – rather than on a canvas of its own. A scatter here needs exactly what those already carry: hovering a point to see which guide it came from, the right-click restyle menu, the export, and a table whose rows can be read against the picture. A second canvas would be a second set of all of that, drifting from the first.

Classes

AnnotationUmapTab

Embed the annotated cells with the controls and score where they sit.

PurityScatter

The embedding, one point per cell, coloured by neighbour purity.

Module Contents

class spacr.qt.widgets.annotation_umap_tab.AnnotationUmapTab(parent: PySide6.QtWidgets.QWidget | None = None)[source]

Bases: PySide6.QtWidgets.QWidget

Embed the annotated cells with the controls and score where they sit.

Parameters:

parent – the owning widget.

Nothing is computed until run() is called. A UMAP over a screen’s cells is seconds of work, and a tab that started it on construction would pay that cost for every user who never opens it.

Build the tab: the embedding beside the per-guide table.

rank is deliberately not offered as a picking method: it takes the top-scoring cells in the well, so its cells landing near the positive controls restates how it chose them rather than testing anything.

The plot and the table are two views of one result and sit side by side behind a divider the user owns – the picture says where the cells landed, the table by how much, and reading one against the other is the whole job.

Parameters:

parent – parent widget, or None.

clear_result(reason: str) → None[source]

Empty the plot and the table, and say why they are empty.

Called on every path that does not end in a verdict. A refusal that left the last run’s scatter on screen would put a picture under a message saying this one means nothing, and the picture is what gets screenshotted.

Parameters:

reason – why there is no result, shown as the plot’s status line.

guide_table(per_guide)[source]

The per-guide rows as a table, purest first.

Parameters:

per_guide – {guide: {"purity", "spread", "cells"}} from spacr.annotation_umap_qc.purity_by_guide().

The guide’s effect is carried alongside its purity because agreeing is the claim being made: two columns a reader can put beside each other are what lets the correlation in the report be checked rather than believed. A guide with no effect gets an empty cell, not a zero – zero is a coefficient somebody measured.

refuse(reason: str, said: str) → None[source]

Show said and leave nothing drawn behind it.

Parameters:
  • reason – short reason shown as the emptied plot’s status line.

  • said – fuller explanation written into the report box.

run() → dict[source]

Tune, embed, score, and report. Returns what it found.

say(text: str) → None[source]

Put text in the report box, replacing what was there.

Parameters:

text – the message for the report box; converted to a string.

set_frame(frame, *, control_labels=None, effects=None) → None[source]

Give the tab the cells to embed.

Parameters:
  • frame – one row per cell, numeric measurement columns.

  • control_labels – POSITIVE / NEGATIVE / None per row.

  • effects – {guide: coefficient} for the agreement test.

class spacr.qt.widgets.annotation_umap_tab.PurityScatter(parent=None)[source]

Bases: spacr.qt.widgets.fast_plots.FastPlot

The embedding, one point per cell, coloured by neighbour purity.

Colour is the reading and position is only the layout: a UMAP’s cluster sizes and the distances between clusters are artefacts, so the picture exists to show WHERE a cell sits among the controls, and the number that says how positive that neighbourhood is comes off the colour scale.

Parameters:

parent – parent widget.

Create the embedding scatter, captioned and axis-labelled.

Parameters:

parent – parent widget, or None.

clear_plot(message: str) → None[source]

Take everything off the plot and say why it is empty.

Parameters:

message – why the plot is empty, shown as its status line.

set_embedding(embedding, purity, guides, marks) → int[source]

Draw embedding, coloured by purity. Returns points drawn.

Parameters:
  • embedding – (n_cells, 2) coordinates.

  • purity – one neighbour-purity value per cell, nan allowed.

  • guides – the guide each cell was annotated with.

  • marks – "PC" / "NC" / None per cell.

A cell with no purity is drawn grey rather than at the bottom of the scale, which is what FastPlot.colour_by_column() does with a missing value – painting a nan dark would invent a measurement.

status() → str[source]

What the plot is saying about itself right now.

The status line is the scatter’s legend – what the colour means and what the shapes are – so it is worth being able to read back rather than only to write.