spacr.plate_measurements¶
One merged measurements frame from the plate rows of the input table.
A row of the regression input table is one plate: its score
CSV, its count CSV and now its measurements database. This module is the
headless half of that feature – given {plate: database path}, the tables
the user ticked and the anchor they chose, it returns the merged frame.
This module adds no merge logic of its own. spaCR already has four places
that join measurement tables, and one of them (io._read_and_join_tables)
aggregates every numeric column with mean, which turns four pathogens’
total area into an average area and a minimum into a mean of minima. This
module composes existing functions instead:
spacr.multi_database.describe_merge()/read_merged()stack the databases – one table at a time, carryingsource_databaseandscreenID, refusing a plate id repeated inside one screen;spacr.merge_tables.roll_up()aggregates each child table onto the anchor, one rule per column out ofspacr.merge_tables.AGGREGATION_RULES;spacr.merge_tables.MergePolicy.how_for()decides the join PER TABLE, because the cardinality differs – a cell has one cytoplasm and many pathogens, and an uninfected cell is still a cell.
There is no sum() or mean() of a measurement column written in this
file, and there must never be one.
Why the two existing functions do not compose by themselves¶
read_merged is many databases, one table. merge_tables is one
database, many tables – and it takes a PATH, so it cannot be handed a frame
that has already been stacked across databases. This feature needs both, so
the composition is: read each chosen table across every database, then roll
the children onto the anchor.
The roll-up keys are the identity columns plus screenID,
source_database and the child’s anchor column. Without screenID, two
screens that share plate1 (which
describe_merge() permits) would collapse into one
parent, which is the pooling multi_database prevents.
What a caller must show the user¶
A merge that silently changed how a measurement was combined produces a number
that is wrong and looks fine, so PlateMerge carries what the panel has
to say: the anchor and the row count, the rows each source contributed, the
measurements columns='common' dropped, the plate ids shared across screens
– and every column that fell through to
spacr.merge_tables.DEFAULT_AGGREGATION because no rule matched it. That
last set is recomputed by re-walking AGGREGATION_RULES
(default_aggregated_columns()) rather than listed anywhere, so it cannot
drift out of step with the rules.
Classes¶
One plate row of the input table and the database attached to it. |
|
The merged frame and the Measurements tab's audit information. |
|
What one chosen table contributed to the merge, and how. |
Functions¶
|
Return the text identifiers that vary within their roll-up group. |
|
Return the object tables present in every attached database. |
|
Split the columns no rule names by the aggregation they will receive. |
|
Return the columns in |
|
One line saying why |
|
Merge every attached plate database into one frame, one row per anchor. |
|
Attached databases that are not on disk, named before the run starts. |
|
Return the plate rows that have a database, in input-table order. |
|
Return the plates that have no database attached. |
Module Contents¶
- class spacr.plate_measurements.PlateDatabase[source]¶
One plate row of the input table and the database attached to it.
- Parameters:
plate – input-table plate label used to map screen metadata and identify missing or duplicate attachments in diagnostics. A blank label normalised from an input row becomes
"row N".path – filesystem path to the plate’s measurements database. An empty value means no database is attached; a nonempty path is checked for existence before table discovery and merging.
- class spacr.plate_measurements.PlateMerge[source]¶
The merged frame and the Measurements tab’s audit information.
- Parameters:
frame – final measurements frame, with one row per surviving anchor object.
anchor – normalized object-table name whose rows define the output cardinality.
attachments – validated plate/database attachments included in the merge, in input order.
tables – per-table audit records in merge order, beginning with the anchor and including any child table skipped because it could not be linked.
- property default_aggregation_columns: Tuple[str, ...][source]¶
Merged columns whose aggregation is the default, no rule matching.
spacr.merge_tables.DEFAULT_AGGREGATIONismean. Columns that no rule matched are listed because a mean may not be the appropriate aggregation for them.
- property dropped_columns: Tuple[str, ...][source]¶
Measurements
columns='common'left out, as merged names.A dropped measurement is a measurement the user came to compare, so it is reported rather than merely defaulted.
- property rows_per_source: Dict[str, int][source]¶
Anchor rows each database contributed to the final frame.
Less than
rows_read_per_sourcewherever an inner join removed objects – a nucleus-less cell, or a pathogen-less one whenkeep_uninfected=False. The pair is what lets a panel say how many rows were dropped instead of only how many are left.
- property rows_read_per_source: Dict[str, int][source]¶
Anchor rows each database held, before any join removed rows.
Plate ids that appear in more than one screen.
Two screens sharing a guide library both have
plate1; withscreenIDin the frame those are two identities, so this is not a collision. They are reported so that a user can see that two screens were merged.
- property sources: Tuple[str, ...][source]¶
The database labels, as
spacr.multi_databasenamed them.
- class spacr.plate_measurements.TableMerge[source]¶
What one chosen table contributed to the merge, and how.
One of these per table the user ticked, including the anchor. It is the record a panel reads to answer “what happened to my numbers”: which aggregation each column got, whether the table was rolled up or joined directly, and which join mode the merge policy selected.
- Parameters:
table – Chosen source object-table name, including the anchor table.
plan – Multi-database read plan used to read this table.
rows – Number of source rows read across the attached databases before joining or child roll-up.
keys – Source identity, join, or grouping columns retained without a table prefix.
how – Child-table join mode returned by
spacr.merge_tables.MergePolicy.how_for(); empty for the anchor or a skipped table.rolled_up – Whether this child table’s rows were aggregated onto the anchor; false for the anchor, a directly joined one-row-per-cell table, or a skipped table.
aggregations – Source-column-to-aggregation mapping actually applied during child roll-up; empty for the anchor, a direct join, or a skipped table.
default_columns – Source columns in
aggregationsthat used the default because no named aggregation rule matched and no override applied.dropped – Source columns absent from at least one attached database and therefore omitted by the common-column read.
note – Explanation when the table was skipped and contributed no merged columns; empty otherwise.
- merged_column(column: str) str[source]¶
The name
columncarries in the merged frame.- Parameters:
column – source-table column name to express in merged-frame form.
The same rule
spacr.merge_tables.roll_up()applies: a join key keeps its name, a column that already starts with the table’s name is left alone –nucleus_areamust not becomenucleus_nucleus_area– and everything else is prefixed with the table it measures.
- spacr.plate_measurements.ambiguous_identifiers(child: pandas.DataFrame, keys: Sequence[str], *, plan: Mapping[str, str] | None = None, overrides: Mapping[str, str] | None = None, examples: int = 1) Dict[str, Dict[str, Any]][source]¶
Return the text identifiers that vary within their roll-up group.
A text identifier that takes
firstmust have one value per group. When it has more, the column is reported rather than resolved silently. A cell with three pathogens has onepath_nameif all three came from the same image; if it has three,firstwould name one of them and the merged row would claim a provenance the data does not support.Only text columns are examined.
object_labelalso takesfirst, by the identity rule, and is expected to differ across the children:spacr.merge_tables.AGGREGATION_RULEScarries the label only so a row can be traced back. A numeric label that varies is the normal case; a text identifier that varies is ambiguous.- Parameters:
child – the many-per-parent table, before the roll-up.
keys – the group keys the roll-up will use.
plan – the aggregation plan, from
spacr.merge_tables.aggregation_plan(). Recomputed when omitted.overrides – the caller’s explicit choices. A column set here is not examined.
examples – how many offending group keys to record per column.
- Returns:
{column: {'groups': n, 'examples': [(key, [values])]}}– empty when every identifier is constant, which is the normal case.
- spacr.plate_measurements.available_tables(attachments: Any) Tuple[str, ...][source]¶
Return the object tables present in every attached database.
- Parameters:
attachments – input-table attachment rows whose existing databases define the table intersection.
The intersection is returned, not the union, because
spacr.multi_database.describe_merge()raises a baresqlite3.OperationalError: no such tablewhen one database lacks the chosen table.- Returns:
the tables in
spacr.merge_tables.OBJECT_TABLESorder, so the picker’s order is the registry’s order rather than sqlite’s, and so a sixth object kind reaches this list by being declared once.png_listis not offered: it holds one row per crop rather than per object, and a crop is not a measurement to aggregate.
- spacr.plate_measurements.classify_default_columns(columns: Sequence[str], kinds: Mapping[str, str] | None = None, *, overrides: Mapping[str, str] | None = None) Dict[str, Tuple[str, ...]][source]¶
Split the columns no rule names by the aggregation they will receive.
- Parameters:
columns – the column names about to be aggregated.
kinds –
{column: 'numeric' | 'text' | 'unknown'}, asspacr.multi_database.column_kinds()returns it. A column absent from the mapping is'unknown'.overrides – the caller’s explicit choices. A column they set is not a fall-through and appears in neither bucket – they chose it.
- Returns:
{'mean': (...), 'identifier': (...), 'unknown': (...)}:meannumeric, no rule matched, so
spacr.merge_tables.DEFAULT_AGGREGATIONis what it takes.identifiertext. It takes
spacr.merge_tables.TEXT_AGGREGATION, not a mean, and whether it is carried or refused depends on the data – seeambiguous_identifiers(), which reads the rows.unknownthe database declared no type, so neither aggregation can be stated in advance. Listed separately so that a column of unknown type is not reported as numeric or text.
- spacr.plate_measurements.default_aggregated_columns(plan: Mapping[str, str], *, overrides: Mapping[str, str] | None = None) Tuple[str, ...][source]¶
Return the columns in
planthat got the default because no rule matched.Recomputed by re-walking
spacr.merge_tables.AGGREGATION_RULES, so this cannot drift out of step with them: a rule added for a measurement tomorrow removes that measurement from this list the same day, and a rule deleted puts it back.A column the caller overrode is not reported however it was set – they chose it. Nor is a text column, which takes
spacr.merge_tables.TEXT_AGGREGATIONbecause text does not add up, not because nobody thought about it.- Parameters:
plan –
{column: aggregation}fromspacr.merge_tables.aggregation_plan().overrides – the caller’s explicit choices, which are excluded.
- spacr.plate_measurements.describe_identifier_refusal(table: str, column: str, detail: Mapping[str, Any]) str[source]¶
One line saying why
columnwas left out, with an example.- Parameters:
table – child table whose identifier could not be carried safely.
column – varying text-identifier column that was omitted.
detail – ambiguity record from
ambiguous_identifiers(), including the affected group count and optional examples.
Written here rather than in the panel so the headless merge and the Qt one say the same sentence.
- spacr.plate_measurements.merge_plate_databases(attachments: Any, tables: Sequence[str] = (), *, anchor: str | None = None, policy: spacr.merge_tables.MergePolicy | None = None, screens: Any = None, columns: str = 'common', report: Callable[[str], None] | None = None, on_ambiguous_identifier: str = 'refuse') PlateMerge[source]¶
Merge every attached plate database into one frame, one row per anchor.
Calls
spacr.multi_database.read_merged()once per chosen table andspacr.merge_tables.roll_up()once per child table. It performs no aggregation of its own, and the join type comes fromspacr.merge_tables.MergePolicy.how_for()per table rather than from a blankethow.- Parameters:
attachments – any shape
plate_databases()accepts. Plates with no database are skipped, not fatal.tables – the object tables to join. The anchor is added if absent.
anchor – the object type one row of the result represents.
cellby default (spacr.merge_tables.DEFAULT_PRIMARY), overridingpolicy.primarywhen both are given.policy – the aggregation and join policy –
na,overrides,consolidate_on_cell,keep_uninfected.screens – optional screen label per plate: a mapping keyed by plate, or a sequence parallel to the attached databases.
columns –
'common'or'union', passed toread_merged.report – called with one line per thing the merge cost, the table named.
on_ambiguous_identifier –
'refuse'(default) leaves out a text identifier that differs within a roll-up group and names it onreport;'first'keeps the first value without a report. Seeambiguous_identifiers(). The Measurements tab applies the same default, so the headless and Qt merges of one screen agree.
- Returns:
the
PlateMerge.- Raises:
MergeRefused – nothing attached, a database that has moved, one database attached to two plates, an anchor that is not one row per cell, a table missing from some database, or – from
read_mergeditself – a plate id repeated inside one screen.
- spacr.plate_measurements.missing_databases(attachments: Any) Tuple[PlateDatabase, ...][source]¶
Attached databases that are not on disk, named before the run starts.
- Parameters:
attachments – input-table attachment rows in any shape accepted by
plate_databases().
- spacr.plate_measurements.plate_databases(attachments: Any) Tuple[PlateDatabase, ...][source]¶
Return the plate rows that have a database, in input-table order.
- Parameters:
attachments –
{plate: path}, a sequence ofPlateDatabase, a sequence of(plate, path)pairs, or the input table’s own rows (mappings carryingplateanddatabase).- Returns:
one
PlateDatabaseper attached row. Rows with no database are left out rather than carried as blanks – seeunattached_plates(), which is where they are named.
- spacr.plate_measurements.unattached_plates(attachments: Any) Tuple[str, ...][source]¶
Return the plates that have no database attached.
- Parameters:
attachments – input-table attachment rows in any shape accepted by
plate_databases().
The regression runs on scores and counts; the database is needed only for the Measurements tab. A plate without one is unavailable there but does not fail the run, and it is listed so that the omission is visible.