itasc.contact_analysis.shape_tables

Aggregated shape tables — index-keyed, materialized views of the products.

Contact Analysis builds one per-position artifact per quantity (a tidy object_table). This module pools those, across the in-scope positions, into a small set of aggregated, index-keyed tables — one file per distinct natural index — written flat at <out_dir>/<name>.csv. They are materialized views: regenerate-whole, never upserted, so a re-aggregate rewrites the file from scratch and CSV stays viable (no concurrent partial write into a shared file). The per-position artifacts remain the normalized source of truth; these tables are a reproducible projection of them.

The pooled tables are label-agnostic: a per-cell subpopulation classification (NLS class_label etc.) is not joined here. A consumer that wants a class split joins it downstream from the dataset that defines it (keyed on experiment_id, position_id, cell_id).

Partitioning principle: one table per quantifier. Each Quantifier that aggregates declares its natural index (table_keys); its tidy object_table is pooled across positions into a table named by the quantifier’s quantity_id. Value columns stay namespaced by quantity_id (cell_shape.area_um2) so a later joined view across tables never has colliding names. The keys and the catalogue metadata (the classification columns — whatever the widget defined) stay bare. (Previously several quantities sharing an index were outer-joined into one wide table — that produced god tables and is gone.)

Generalizes the old one-quantity-at-a-time pooling (napari…plots._pooling.pool_quantity) to “all quantities of an index, persisted.” Backend-only (no Qt) so headless / batch runs and the standalone wheel use it unchanged.

Module attributes

PROVENANCE_NAME

The single run-level provenance sidecar written beside the pooled tables.

Functions

aggregate(records[, out_dir, params, quantities])

Regenerate every aggregated table for the in-scope records; return the table name → written CSV path map.

build_table(name, records, *[, params])

Pool the quantifier name's per-position table across records into one frame.

catalogue_root(records)

A stable home for the aggregated tables: the common ancestor of the in-scope positions' folders.

read_table(path)

Read an aggregated-table CSV, restoring integer dtype on the key columns (frame / *_id) so downstream group-bys match the in-memory build.

shape_table_registry()

Map table name → ShapeTableSpec.

table_for_quantity(quantity_id)

The aggregated table quantity_id lands in — its own quantity_id when it aggregates (declares table_keys), else None (contacts and other non-aggregated quantities).

table_path(out_dir, name)

Where table name's CSV lives: flat under out_dir.

Classes

ShapeTableSpec(name, keys, quantity_ids)

One aggregated table: a single quantifier's pooled object_table — the table name (its quantity_id), the index keys, and the contributing quantity (quantity_ids carries the one id, kept a tuple for the pooling machinery).

class itasc.contact_analysis.shape_tables.ShapeTableSpec(name, keys, quantity_ids)[source]

Bases: object

One aggregated table: a single quantifier’s pooled object_table — the table name (its quantity_id), the index keys, and the contributing quantity (quantity_ids carries the one id, kept a tuple for the pooling machinery).

Parameters:
name: str
keys: tuple[str, ...]
quantity_ids: tuple[str, ...]
itasc.contact_analysis.shape_tables.shape_table_registry()[source]

Map table name → ShapeTableSpec. Each aggregating quantifier — one that declares a table_keys index — is its own table, named by its quantity_id and keyed by its own grain. No quantifier shares a table with another (no god tables).

Return type:

dict[str, ShapeTableSpec]

itasc.contact_analysis.shape_tables.table_for_quantity(quantity_id)[source]

The aggregated table quantity_id lands in — its own quantity_id when it aggregates (declares table_keys), else None (contacts and other non-aggregated quantities).

Return type:

str | None

Parameters:

quantity_id (str)

itasc.contact_analysis.shape_tables.build_table(name, records, *, params=None)[source]

Pool the quantifier name’s per-position table across records into one frame.

For each in-scope record the quantifier’s tidy table is computed in memory via compute_object_table() (value columns namespaced by quantity_id) and the catalogue metadata is stamped; the per-position frames are then concatenated. params carries the shared build knobs (e.g. fov_area_mm2, pixel_size_um) — the same ones build_quantities() threads into build, so a param-gated quantifier (cell shape needs pixel_size_um) pools consistently with what was actually built. The result is label-agnostic (no class_label join). Empty when nothing computes. In-memory only — see aggregate() to persist.

Return type:

DataFrame

Parameters:
itasc.contact_analysis.shape_tables.read_table(path)[source]

Read an aggregated-table CSV, restoring integer dtype on the key columns (frame / *_id) so downstream group-bys match the in-memory build.

Return type:

DataFrame

Parameters:

path (Path | str)

itasc.contact_analysis.shape_tables.aggregate(records, out_dir=None, *, params=None, quantities=None)[source]

Regenerate every aggregated table for the in-scope records; return the table name → written CSV path map.

Materialized-view semantics: each table is rebuilt whole and its CSV overwritten (a previously-written table for a now-out-of-scope position is simply not regenerated with that position). A table that pools to no rows is skipped (no empty file). out_dir defaults to catalogue_root(). params is forwarded to build_table() (the shared build knobs the param-gated quantifiers need to compute at all).

quantities restricts which tables are written: None (the default) writes every registered table; a sequence writes only those whose quantity_id it names (unknown / non-tabular ids — e.g. the contacts producer — are simply absent from the table registry and ignored). An empty sequence writes nothing.

Return type:

dict[str, Path]

Parameters:
itasc.contact_analysis.shape_tables.table_path(out_dir, name)[source]

Where table name’s CSV lives: flat under out_dir.

Return type:

Path

Parameters:
itasc.contact_analysis.shape_tables.catalogue_root(records)[source]

A stable home for the aggregated tables: the common ancestor of the in-scope positions’ folders. Falls back to the first position’s parent, then to the current directory when no position folder is known.

Return type:

Path

Parameters:

records (Sequence[dict])

itasc.contact_analysis.shape_tables.PROVENANCE_NAME = 'provenance.json'

The single run-level provenance sidecar written beside the pooled tables. The cheap quantities are computed in memory and never persisted per-position, so their old per-position .provenance.json sidecars are gone; this one file records how a whole aggregate run was produced instead.