spacr.cell_montage¶
Select microscopy objects associated with a regression coefficient.
The input consists of a guide- or gene-level coefficient, per-object
measurements and per-well guide fractions. Because pooled screens do not
identify which individual cell contains a guide, the result represents cells
whose measured phenotype is consistent with the fitted effect; it does not
assign a genotype to individual cells. MontagePlan includes
INFERENCE_NOTICE in every generated caption to preserve this
distinction.
Selection proceeds in three stages:
wells_for_coefficient()identifies wells with a non-zero fraction of the guide or gene. Gene-level selection sums guide fractions by default;select_montage_per_guide()evaluates guides separately.score_window()definestarget = baseline + effectand retains objects withinhalf_widthsrobust scales of the target, ordered by|score - target|. The baseline and scale are calculated across the supplied object population rather than separately for each gene.objects_to_show()selectsround(n_objects_in_well * guide_fraction_in_well)objects from each well. Zero-contribution wells remain in the plan for reporting.
Images are obtained through spacr.crops.PngCropSource for exported
PNG crops or spacr.crops.MergedCropSource for crops extracted from
merged/<fov>.npy. resolve_montage_crop_source() reports when neither
source is available. Per-well guide fractions are read from
regression_data.csv; the aggregate files grna_well.csv and
well_grna.csv do not contain the required well-by-guide fractions.
Exceptions¶
The gene or guide named by the coefficient is not in the count data. |
|
The object frame carries no usable per-object classification score. |
|
The requested montage cannot be selected from what was supplied. |
Classes¶
One point on the regression plot: a gene or a guide, and its effect. |
|
Which crop source will draw a montage, or why none can. |
|
Store selected objects and values required to document selection. |
|
Describe the inputs and crop shapes available through one pixel route. |
|
The band of classification scores a coefficient implies, and its rule. |
|
What one well contributed to the montage, and why that number. |
Functions¶
|
Return |
|
Load the gene-by-measurement effects grid stored beside a run. |
|
Build per-well guide fractions directly from count CSV files. |
|
Return the guides a coefficient covers, in sorted order. |
|
Return the per-object rows a montage selects from, out of one database. |
|
Validate the selected route before extracting any crops. |
|
Return |
|
Read the per-well guide fractions a montage needs. |
|
Pick the crop source for a montage, and never raise for "none". |
|
Round |
|
Return the band of scores the coefficient |
|
Return the objects to show behind one coefficient. |
|
Return one |
|
Return the wells whose count data reports |
|
Write a sweep's effects grid beside the run. Returns the path. |
Module Contents¶
- exception spacr.cell_montage.CoefficientNotFound[source]¶
Bases:
MontageErrorThe gene or guide named by the coefficient is not in the count data.
Initialize self. See help(type(self)) for accurate signature.
- exception spacr.cell_montage.MissingScores[source]¶
Bases:
MontageErrorThe object frame carries no usable per-object classification score.
Initialize self. See help(type(self)) for accurate signature.
- exception spacr.cell_montage.MontageError[source]¶
Bases:
ValueErrorThe requested montage cannot be selected from what was supplied.
Initialize self. See help(type(self)) for accurate signature.
- class spacr.cell_montage.Coefficient[source]¶
One point on the regression plot: a gene or a guide, and its effect.
- Parameters:
name – the gene or guide name as the count data spells it.
effect – the fitted coefficient.
level –
'gene'or'grna'.guides – the guides the coefficient covers – one for a guide-level coefficient, all of the gene’s for a gene-level one.
- class spacr.cell_montage.CropSourceChoice[source]¶
Which crop source will draw a montage, or why none can.
The tab needs both answers in the same shape: the design says a tab that cannot be filled must say why rather than be absent, so “there is no source” is a value here and not an exception.
- Parameters:
source – the
spacr.crops.CropSource, orNone.kind –
'png','merged', or''when unavailable.reason – why that source was picked, or why none could be.
available – whether a montage can be drawn at all.
requirements – what that route needs and what it can offer –
RouteRequirements, checked up front so a missing channel list is reported as a missing channel list rather than surfacing later as a crop that will not cut.Nonewhen nothing asked.
- class spacr.cell_montage.MontagePlan[source]¶
Store selected objects and values required to document selection.
- Parameters:
coefficient – the point that was clicked.
window – the score window that was applied.
wells – one
WellSelectionper well the count data reported the coefficient present in, including wells that contributed no selected objects.objects – the selected object rows, in well order then by distance to the target, with
montage_distance,montage_wellandmontage_rankadded.score_column – the per-object score the selection used.
guide_aggregation –
'sum'or'separate'.guides – the guides the coefficient covers.
source_kind –
'png'/'merged'/''– which crop source will draw these, when one was resolved.source_reason – why that source was picked.
cap – the cap that was applied.
n_before_cap – how many objects the rule selected before the cap.
notes – everything the caption has to disclose that is not a per-well count.
- arithmetic() str[source]¶
The whole sum, in words a reader can check.
Everything a reader would need to reproduce the selection by hand: where the baseline came from, what the target is and why, how wide the window is and in what units, and
round(n x fraction)for each well with the total they add to.
- caption() str[source]¶
Return the caption the montage must carry.
The caption includes the wells, score window, selection settings, count calculation, crop source, zero-contribution wells and the notice that guide membership is inferred rather than observed.
- rows() List[Dict[str, Any]][source]¶
Return the selected objects as plain dicts for a crop source.
spacr.crops.PngCropSourceandMergedCropSourceboth take a row mapping, so this is what the tab hands tosource.get_many(...).
- settings_line() str[source]¶
Return the selection settings, including their default values.
These settings apply to the complete screen rather than individual genes. Non-default values are reported explicitly in the montage.
- Returns:
one or two lines – what was in force, and (only when something was) what was changed from the default.
- property is_empty: bool[source]¶
True when no object survived the selection.
The tab shows the caption anyway: “no objects” with the wells and the window that produced none is an answer, and an empty tab is not.
- property zero_wells: Tuple[WellSelection, ...][source]¶
The wells that reported the guide but contributed no object.
- class spacr.cell_montage.RouteRequirements[source]¶
Describe the inputs and crop shapes available through one pixel route.
Requirements are validated before extraction.
'merged-mask'requires image channels, a mask array and either a bounding box or object id; object identifiers permit object-shaped crops.'merged-bbox'requires tabular coordinates and image channels and supports bounding-box crops only.'png'uses the shape created whenmeasure_cropexported the image.- Parameters:
route –
'png','merged-mask','merged-bbox', or'none'.shapes – Crop shapes from
CROP_SHAPESthat the route can produce. This is empty for'png'because exported PNGs retain the shape used when they were created.missing – Required inputs that are absent, expressed as user-facing descriptions.
assumed – Missing values replaced by defaults and reported in the status caption rather than treated as errors.
detail – Explanation of how the route was identified.
- offers(shape: str) bool[source]¶
True when
shapeis a crop this route can really produce.- Parameters:
shape – crop shape to check against this route’s capabilities.
- class spacr.cell_montage.ScoreWindow[source]¶
The band of classification scores a coefficient implies, and its rule.
- Parameters:
target – the per-object score the coefficient implies.
low – the window’s lower bound, inclusive.
high – the window’s upper bound, inclusive.
baseline – the score of an object carrying no guide of interest.
baseline_source –
'screen_median'when derived from every object supplied,'given'when the caller passed one.scale – the robust scale (MAD-sigma) the half-width is measured in.
half_widths – how many scales wide each side is.
n_scored – how many objects the baseline and scale were computed from – the whole supplied frame, not the selected wells.
observed_low – smallest score seen across all objects.
observed_high – largest score seen across all objects.
degenerate – True when every score is identical, so the window has no width to speak of and admits everything.
- contains(scores) numpy.ndarray[source]¶
Return the boolean mask of
scoresthat fall inside the window.- Parameters:
scores – any array-like of per-object scores. Non-finite entries are outside the window whatever the bounds are – a NaN score is a missing measurement, not a near miss.
- property target_is_observable: bool[source]¶
True when the implied score lies inside the observed score range.
False is a real finding, not an error: it says no object anywhere in the screen scores anything like what the coefficient implies, so the montage is showing the least-far objects rather than close ones.
- class spacr.cell_montage.WellSelection[source]¶
What one well contributed to the montage, and why that number.
- Parameters:
well – the well key as a display string.
fraction – the guide’s fraction in this well, from the count data.
n_objects – how many objects the object frame holds for this well – the population
round(n x fraction)is computed from.n_reported –
cell_countas the count data recorded it, orNone. Kept besiden_objectsrather than instead of it, because when they disagree the montage has to say so.n_expected –
round(n_objects x fraction)– the count rule.n_in_window – how many of this well’s objects fall inside the score window at all.
n_selected – how many were actually taken, after the window and after any cap.
note – why
n_selectedis notn_expected, or''.share – normalized fraction actually used for
n_expected;Nonemeans the rawfractionwas used.
- spacr.cell_montage.coefficient_level(counts: pandas.DataFrame, name: str, *, guide_column: str = 'grna', gene_column: str = 'gene') str[source]¶
Return
'grna'or'gene'forname, read off the count data.A guide wins a tie. A library where one string is both a guide name and a gene name is pathological, and resolving it as the gene would silently widen the montage to that gene’s other guides.
- Parameters:
counts – the per-well count data.
name – the coefficient’s name.
guide_column – the guide column, default
'grna'.gene_column – the gene column, default
'gene'.
- Raises:
CoefficientNotFound – the name is in neither column.
- spacr.cell_montage.effects_grid_from_results(path) pandas.DataFrame | None[source]¶
Load the gene-by-measurement effects grid stored beside a run.
- Parameters:
path (path-like) – Results CSV or directory containing
EFFECTS_GRID_FILE.- Returns:
pandas.DataFrame or None – Effects indexed by guide, or
Nonewhen the grid is absent, empty, or unreadable.
Notes
The persisted grid lets multivariate montage selection work across application sessions. Callers may fall back to single-score attribution when no valid grid is available.
- spacr.cell_montage.fractions_from_counts(paths: Sequence[str]) pandas.DataFrame[source]¶
Build per-well guide fractions directly from count CSV files.
A guide’s fraction in a well is its share of that well’s reads:
fraction = count / (sum of count over the well)
This is the same calculation applied by
spacr.ml.process_reads()before fitting.regression_data.csvpersists the resulting join but is not required as the source of the fractions.- Parameters:
paths – the count CSVs from the regression input table – the
countcolumn of the rows the database provider already returns, so no new plumbing reaches them.- Returns:
one row per (well, guide) with
prc,grnaandfraction, andgenewhen the counts carry it.- Raises:
MontageError – no readable counts, or a file short of a column.
Well totals contain one row per well and are merged with
validate='many_to_one'to prevent duplicated guide rows.
- spacr.cell_montage.guides_for_coefficient(counts: pandas.DataFrame, name: str, *, level: str | None = None, guide_column: str = 'grna', gene_column: str = 'gene') List[str][source]¶
Return the guides a coefficient covers, in sorted order.
- Parameters:
counts – the per-well count data.
name – the coefficient’s name.
level –
'gene'/'grna';Nonereads it off the data withcoefficient_level().guide_column – the guide column.
gene_column – the gene column.
- Returns:
one guide for a guide-level coefficient, the gene’s guides for a gene-level one.
- spacr.cell_montage.load_montage_objects(db_path: str, *, object_type: str = 'cell', score_column: str = 'pred', table: str = 'png_list', src: str | None = None, scores: Any = None, verbose: bool = False) pandas.DataFrame[source]¶
Return the per-object rows a montage selects from, out of one database.
Reads
png_list– which is wherespacr.predictions.merge_cv_predictions()writes the per-object classification score – and hands it tospacr.io.crop_rows_from_png_list(), which is the join that recoverspath_nameand the integerobject_labela merged crop is cut by. So the frame that comes back serves both crop sources:png_pathforPngCropSource,path_nameplusobject_labelforMergedCropSource.- Parameters:
db_path – the
measurements.db.object_type – which crop mode’s rows to read.
score_column – the per-object classification score column.
table – the table holding the crops;
'png_list'.src – the screen’s current folder. Defaults to the plate folder the database sits in. Crop paths are recorded absolute at crop time, so a screen that has moved computer – or a NAS mounted somewhere else – carries 60,000 paths that no longer exist while every file is present; see
spacr.portable_paths. Only paths that resolve to an existing file are rewritten.scores – where the per-object classification scores are, when the database has none: a frame, a path, or several paths – the score CSVs the run was fitted on. Used ONLY when
png_listcarries no score column, and never written back.verbose – let the io join report the rows it could not place.
- Returns:
the object frame, with
prccomposed when the well keys are there.- Raises:
MontageError – the database is missing or has no such table.
MissingScores – the table has no score column, or no finite score in it – i.e. classification has not been merged into this database.
- spacr.cell_montage.montage_route_requirements(source, objects=None, *, object_type: str = 'cell', channels: Sequence[int] | None = None, channels_declared: bool | None = None) RouteRequirements[source]¶
Validate the selected route before extracting any crops.
- Parameters:
source – the
spacr.crops.CropSource(or theCropSourceChoiceholding one) that will draw the montage.Noneis the “no source” answer and comes back as route'none'.objects – the per-object frame the crops will be cut from. Its COLUMNS are what decide between the two merged routes – an object id means the mask can be followed, only a bounding box means it cannot.
Noneskips the row checks and reports the route the source alone implies.object_type – which mask plane the crop is cut by.
channels – the channels the user asked for, if any.
channels_declared – whether a channel list exists at all – typed by the user or recorded by the run.
Noneinfers it fromchannels. False is the case the request names specifically: A USER MISSING A CHANNEL LIST IS TOLD THAT, not told there is no source.
- Returns:
the
RouteRequirements.
- spacr.cell_montage.objects_to_show(n_objects: int, fraction: float) int[source]¶
Return
round(n_objects * fraction)– the montage’s count rule.This is the expected count implied by the guide fraction. A pooled design supports an expected count but not the identity of guide-bearing objects.
- Parameters:
n_objects – how many objects the well actually has to draw from. Negative counts are refused; zero is legal and yields zero.
fraction – the guide’s fraction in that well, in
[0, 1]. A fraction outside that range is refused rather than clipped – it means the count data and the well key did not line up, and a clipped montage would hide that.
- Returns:
how many objects that well contributes.
- spacr.cell_montage.read_well_guide_fractions(path: str) pandas.DataFrame[source]¶
Read the per-well guide fractions a montage needs.
- Parameters:
path – a regression results folder, or the CSV itself. A folder is resolved to
regression_data.csv.- Returns:
the frame, validated to carry a well key,
grna,geneandfraction.- Raises:
MontageError – the path names
grna_well.csvorwell_grna.csv– refused by name, with what each actually holds, because they are the obvious guess and neither can answer the question; or the file is missing, or short of a needed column.
- spacr.cell_montage.resolve_montage_crop_source(src, *, object_type: str = 'cell', prefer: str | None = None, objects: pandas.DataFrame | None = None, channels: Sequence[int] | None = None) CropSourceChoice[source]¶
Pick the crop source for a montage, and never raise for “none”.
A thin wrapper on
spacr.crops.resolve_crop_source(), which is the module that already knows how to choose between the exported PNGs andmerged/*.npyand already says which it chose. The wrapper exists for exactly one reason: a missing source has to arrive as a sentence the tab can display, not as an exception it has to catch to stay on screen.- Parameters:
src – a settings mapping (with
src, optionallycrop_source) or the experiment root / itsmergedfolder.object_type – which object the crops are cut by.
prefer – force
'png'or'merged'.objects – the per-object frame, so the ROUTE’s own requirements can be checked up front – an object id means the mask can be followed, a bounding box alone means it cannot. Left out, the requirements are those the source alone implies.
channels – the channels the caller asked for, so “no channel list anywhere” can be reported as exactly that.
- Returns:
a
CropSourceChoice;availableis False, with the reason, when neither source exists.
- spacr.cell_montage.round_half_up(value: float) int[source]¶
Round
valueto the nearest integer, halves away from zero.Deliberately not
round()ornumpy.round(), both of which round halves to even: a well whosen * fractionis 2.5 would then contribute 2 while an otherwise identical well at 3.5 contributed 4. That is a montage whose per-well counts depend on the parity of a number the caption calls “round(n x fraction)”, which no reader could reconstruct.- Parameters:
value – the number to round. Non-finite input raises rather than producing a count.
- Returns:
the rounded integer.
- spacr.cell_montage.score_window(objects: pandas.DataFrame, effect: float, *, score_column: str = DEFAULT_SCORE_COLUMN, half_widths: float = WINDOW_HALF_WIDTHS, baseline: float | None = None, baseline_label: str | None = None) ScoreWindow[source]¶
Return the band of scores the coefficient
effectimplies.THE RULE, stated once and applied identically to every coefficient:
the target is
baseline + effect. Under the well-level model a well’s score isbaseline + fraction * effect, so a well made entirely of cells carrying the guide would scorebaseline + effect– which is the score one such cell implies.the baseline is the median per-object score over every object supplied. One number for the screen, so it does not move when the gene does.
“closest” is smallest
|score - target|, and the window istarget +/- half_widths * scalewherescaleis1.4826 * MADof the same screen-wide score distribution.
- Parameters:
objects – every object available to the montage – all wells, not just the coefficient’s. Passing only the selected wells is what makes a window per-gene, so the whole frame is the argument.
effect – the fitted coefficient.
score_column – the per-object classification score.
half_widths – the window’s half-width in robust scales. Overriding
WINDOW_HALF_WIDTHSis allowed and is written into the caption, because a widened window changes which cells a reader is looking at.baseline – use this baseline instead of the screen median – the fitted intercept, say. Recorded as
'given'.baseline_label – what to record as the baseline’s SOURCE when one is given, e.g.
'the model's fitted intercept'. The caption reads this back, so a montage centred on the intercept says so rather than sayinggiven, which names no source at all. Ignored whenbaselineis None – the screen median has one source and it is already named.
- Raises:
MissingScores – no usable score column, or no finite score in it.
MontageError – a non-positive or non-finite
half_widths.
- spacr.cell_montage.select_montage(objects: pandas.DataFrame, counts: pandas.DataFrame, name: str, effect: float, *, level: str | None = None, score_column: str = DEFAULT_SCORE_COLUMN, half_widths: float = WINDOW_HALF_WIDTHS, baseline: float | None = None, baseline_label: str | None = None, cap: int = MAX_OBJECTS, guide_aggregation: str = 'sum', guide_column: str = 'grna', gene_column: str = 'gene', fraction_column: str = 'fraction', crop_source: CropSourceChoice | None = None, guides: Sequence[str] | None = None, show_all: bool = False, picking: str = 'rank', effects: Mapping[str, float] | None = None, effects_grid: pandas.DataFrame | None = None, exclude_grnas: Sequence[str] | None = None, normalise_fraction: bool = True, threshold: float = 0.55) MontagePlan[source]¶
Return the objects to show behind one coefficient.
The whole selection, in the order the design states it: the wells the count data reports the guide present in, the objects in those wells whose classification score is closest to what the coefficient implies, and
round(n x fraction)of them per well.- Parameters:
objects – every object available – all wells, not just the coefficient’s. The window’s baseline and scale are computed from this whole frame precisely so that they do not move when the gene does, so pre-filtering it to the coefficient’s wells is the one thing that makes the window tunable.
counts – the per-well count data (
regression_data.csv).name – the coefficient’s gene or guide name.
effect – its fitted coefficient.
level –
'gene'/'grna';Nonereads it off the counts.score_column – the per-object classification score.
half_widths – the score window’s half-width in robust scales.
baseline – an explicit baseline instead of the screen median.
baseline_label – what to record as that baseline’s source, so the caption names it –
'the fitted intercept'rather thangiven.cap – the largest montage to return; the objects closest to the implied score survive, and the caption says what was trimmed.
guide_aggregation –
'sum'or'separate'.'separate'is served byselect_montage_per_guide(); passing it here is an error rather than a silent sum.guide_column – the guide column in
counts.gene_column – the gene column in
counts.fraction_column – the per-well guide fraction column in
counts.crop_source – an already-resolved
CropSourceChoice, so the plan can say which source will draw it.guides – override the guides the coefficient covers – used by
select_montage_per_guide()to plan one guide at a time.
- Returns:
the
MontagePlan.- Raises:
CoefficientNotFound – no well reports the coefficient present.
MissingScores – the object frame carries no usable score.
- spacr.cell_montage.select_montage_per_guide(objects: pandas.DataFrame, counts: pandas.DataFrame, name: str, effect: float, **kwargs) List[MontagePlan][source]¶
Return one
MontagePlanper guide of a gene-level coefficient.Keeping guides separate shows whether each guide selects comparable cells, complementing the gene-level montage built from summed guide fractions.
- Parameters:
objects – every object available, as for
select_montage().counts – the per-well count data.
name – the gene (or guide) name.
effect – fitted gene-level coefficient shared by every guide plan; guides differ only in the wells and cells they select.
kwargs – forwarded to
select_montage().
- Returns:
one plan per guide, in guide order. A guide whose wells report it nowhere is skipped, and the guides that produced a plan are on each plan’s
guides.
- spacr.cell_montage.wells_for_coefficient(counts: pandas.DataFrame, name: str, *, level: str | None = None, guide_aggregation: str = 'sum', guide_column: str = 'grna', gene_column: str = 'gene', fraction_column: str = 'fraction') pandas.DataFrame[source]¶
Return the wells whose count data reports
namepresent.Step 1 of the selection. A gene’s guides are summed by default – the gene-level coefficient describes the fraction of the well carrying ANY guide against the gene – and the sum is refused if it exceeds 1, because that means the same well was counted twice.
- Parameters:
counts – per-well count data: a well key (
prc, or all ofplateID/rowID/columnID),grna,geneandfraction.regression_data.csvis that frame;grna_well.csvandwell_grna.csvare NOT – seeread_well_guide_fractions().name – the coefficient’s name.
level –
'gene'/'grna';Nonereads it off the data.guide_aggregation –
'sum'(one fraction per well, the default) or'separate'(agrnacolumn is kept and one row per well/guide comes back, forselect_montage_per_guide()).guide_column – the guide column.
gene_column – the gene column.
fraction_column – the per-well guide fraction column.
- Returns:
one row per well (or per well/guide) with the well key,
fraction, andcell_countwhen the count data carries it.- Raises:
CoefficientNotFound – no well reports the coefficient present.
- spacr.cell_montage.write_effects_grid(effects, folder) str[source]¶
Write a sweep’s effects grid beside the run. Returns the path.
- Parameters:
effects – gene-by-measurement effects table to persist.
folder – run directory in which to write the grid.
Written by the sweep so the montage can read it later – and in another session, which is the half a panel-to-panel handover cannot do.