starplast.umap_gallery
Pregenerated maps, and how well every label maps onto each of them.
The central map is one answer to "which measurements should place a gene": all of them, balanced by
block. It is not the only reasonable answer, and a label that fails to separate on it may separate
cleanly on a map built from one kind of evidence -- stage transcription, knockout fitness, protein
abundance. So a gallery of maps is built ahead of time for each organism and shipped, in six groups
(GROUPS, and recipes builds them in this order):
- all measurements at three
n_neighborssettings (10, 25, 60), because the neighbourhood size decides whether a map shows many small islands or a few continents, and which one a label prefers is itself informative. These three are the gallery's fixed reference maps: their settings are declared, not searched, so the same three maps ship whatever a structure search would have picked; - all but one family -- every measurement except localization, except transcription, except fitness, and so on -- so a label can always be scored on a map that never saw the kind of measurement that restates it;
- one map per evidence family -- the slot catalogue's axis (
strategies.Context.family_of): transcription, translation, protein abundance, fitness, sequence, and so on; - one map per individual experiment block -- each screen, each expression atlas, each proteomics
set, the structure and sequence blocks -- wherever the block has at least
MIN_BLOCK_COLUMNScolumns andMIN_SET_GENESgenes measured in enough of them. A block with one or two columns has no 3D map worth drawing and is covered by its family instead; - pairs and triples of families that make a biological question (
COMBINATIONS): transcription + translation is expression end to end, fitness + protein abundance asks whether what a knockout costs tracks how much protein there is, and so on. Each carries the reason it is in the gallery.
Every map is built from measurements only. No categorical label column is an input (labels are what
is scored), and the recipe of each map is stored beside it. Each map is clustered with HDBSCAN through
clustering.cluster, as the Clusters tab does (see CLUSTERING for why "leaf" and not the Clusters
tab's opening 25/25 "eom").
Maximising structure. Except for the three fixed reference maps, every candidate feature set is
SEARCHED: MAP_GRID UMAP settings x GALLERY_CLUSTER_GRID clusterings, and the combination with the
best label-free structure score (structure) is the one that ships. No label is consulted, so the
search cannot tune a map into recovering the thing it will be scored on. The search is successive
halving, as search.tune_umap does it -- every UMAP setting ranked on a SEARCH_SAMPLE sample,
only the best SEARCH_KEEP rebuilt over all the recipe's genes -- because an embedding costs about
a hundred times a reclustering. Every setting tried, and its score, is stored in the manifest beside
the map that won.
The scores. For a label (a categorical column, e.g. compartment) and a map:
- Categories map to clusters -- for each category of the label, the cluster that best recovers it
is found and its F1 taken (precision: share of the cluster's labelled genes in the category;
recall: share of the category's genes in that cluster). The label's score is the mean of these F1
values weighted by category size, as
search.score_recoverydoes. One difference, stated: genes HDBSCAN leaves unclustered still count in a category's size, so a map that calls most genes noise cannot score high recall on the few it clustered. Mean precision and mean recall are reported beside it. - Best single category -- the one category that maps best, scored on the F1 of the Wilson 95% lower bounds of its precision and recall. A 2-gene category that sits alone in one cluster has precision 2/2 and recall 2/2, but lower bounds of 0.34 each, so it scores 0.34 rather than 1.0; a 200-gene category at 0.9/0.9 keeps about 0.86. Size therefore counts through evidence, not through an arbitrary weight. The category chosen is the one whose bound beats its OWN shuffled-label chance by the most: on a map with one giant cluster, the majority value of a yes/no label has a high bound by sheer size and exactly as high a bound when shuffled, and it must not win for that.
- Chance -- both scores recomputed with the label's values shuffled over the same genes (five shuffles, fixed seeds) and averaged. "Skill" is (score - chance) / (1 - chance): 0 is what the map's cluster sizes give any label, 1 is perfect.
- Circular -- whether the map was built from a column in the label's leakage closure
(
search.excluded_for): a map built from hyperLOPIT probabilities recoveringcompartmentis expected, not discovered.
The same scoring runs live for a map built in the application (score_map).
The Wilson score interval's lower bound for k successes in n trials (0 where n is 0).
(score - chance) / (1 - chance): 0 at chance, 1 at perfect, negative below chance.
Works elementwise on arrays; NaN where either is missing or chance is already perfect.
Every category of a label against one clustering: best cluster, P, R, F1, bounded F1, skill.
truth and clusters are aligned per gene (the genes placed on one map); -1 in clusters is
HDBSCAN noise. Unlabelled genes are ignored; unclustered labelled genes count as missed. Sorted
by skill, the size-aware score against its own shuffled-label chance level -- the order the
best single category is chosen in.
The scores of one label on one clustered map, with the shuffled-label chance level.
Returns category_f1 / category_precision / category_recall (size-weighted over categories),
the chance level and skill of category_f1, and the best single category: the one whose
size-aware score (best_f1_lower, the F1 of the Wilson lower bounds) beats its OWN chance
level by the most. Chosen on skill rather than on the raw bound, because on a map with one
giant cluster the majority value of a yes/no label scores a high bound by sheer size, and
exactly as high with the labels shuffled.
Mean silhouette of the clustered points, rescaled to 0..1 (0.5 is no separation at all).
On the map's own coordinates, over clustered points only -- HDBSCAN noise is not a cluster and scoring it as one would punish exactly the maps that correctly refuse to place a gene. NaN when there is nothing to measure (fewer than two clusters).
How much structure a map has, WITHOUT looking at any label. The gallery's search ranks on this.
Two things are measured, because either alone is gameable:
search.map_quality-- the project's own measure, and the one strategy 05 tunes on: the share of genes clustered times the evenness of the cluster sizes, gated byclustering.degenerateso a configuration that merely bisected the cloud scores zero however confident it looked. It is about the PARTITION and says nothing about the geometry: clusters can be even and cover everything while overlapping completely.separation-- the mean silhouette of the clustered points, which is about the geometry and says nothing about coverage: two crisp clusters holding 5% of the proteome score beautifully.
structure is their GEOMETRIC mean, so a map has to be good at both to win, and a zero in either
is a zero overall. It is not a label score: no label is read here, which is what lets the search
tune a map that will afterwards be scored against labels honestly.
A block key as a readable phrase, with the space's own prefix removed.
The prefix comes from the registry (organisms), never from a typed species code: slot keys are
f"{code}_{name}" by construction, so every registered code is tried.
The gallery for one organism's context: a list of dicts (id, title, blocks, group, ...).
Blocks are the display recipe's slot blocks (embedding.default_spec), grouped into families by
the slot catalogue's axis. Only measurements are inputs: the slot catalogue's feature role
already excludes label columns, and any categorical column that slipped through is refused.
Six groups, in GROUPS order: the three fixed "all measurements" reference maps, an "all but"
map per family, a map per family, a map per individual experiment block, and the pairs and
triples of COMBINATIONS. Every recipe but the three reference maps carries tune: True, which
asks build_map to search MAP_GRID x GALLERY_CLUSTER_GRID for the best-structured version.
Positions of the genes a recipe places: all of them, or those measured often enough.
Search MAP_GRID x GALLERY_CLUSTER_GRID for the best-structured map of one recipe.
Successive halving, as search.tune_umap does it and for the same reason: an embedding of a
whole proteome costs several times one of 1,500 genes, and most settings are not worth paying
the full price for twice. Every UMAP setting is ranked on the sample, the best keep are rebuilt
over all the recipe's genes, and the clustering grid is searched on each finished embedding.
Ranked on structure alone -- no label is read -- and returns the winner together with every
setting tried and its score, so the manifest can record what the choice was made against.
Embed and cluster one recipe. Returns rows, xyz (float32), clusters (int16) and provenance.
A recipe with tune searches MAP_GRID x GALLERY_CLUSTER_GRID and ships the best-structured
combination (search_settings); one without is built exactly at its declared settings. Either
way the map's structure and every setting tried are recorded in info.
Every label's scores on one clustered map (the gallery's, or one built in the app).
rows are the positions in ctx.nodes the map places, clusters one label per placed gene.
columns, when known, are the map's source columns, used for the circularity flag; None leaves
it unknown.
The label x map table for one organism. maps is map id -> {rows, clusters, info}.
Write the gallery: coordinates (npz), manifest (json), scores (tsv). Returns the paths.
built is organism -> map id -> build_map() result; gene_ids organism -> the node table's
gene ids, stored so a loader can refuse a gallery built over a different table.
The shipped gallery, loaded lazily: manifest, per-map coordinates and clusters, scores.
Point at a directory holding the three gallery files (the package data by default).
The map records (recipes, sizes, cluster counts) for one organism, in gallery order.
The node table's gene order the gallery was built over.
rows (positions in the build's node table), xyz (float32) and clusters of one map.
One map re-indexed onto another table's gene order (genes it lacks are dropped).
The window's table normally IS the build's table; this is what keeps a gallery usable when it is not (a rebuilt cache with genes added or reordered) instead of drawing genes in the wrong places.
The packaged gallery, shared.
The organisms a gallery is built for: every parasite space whose table ships.
One line per map for a label, best first: the text form of the panel's table.