itasc.contact_analysis.contacts

Cell-cell contact quantifier: edges, T1 events, neighbourhood & density.

The contacts domain logic that used to live at the top-level itasc.contact_analysis package. It is one quantifier within itasc.contact_analysis; its public API is re-exported from the package root for stability. All of it is label-agnostic.

itasc.contact_analysis.contacts.label_contacts(analysis, labels, *, unclassified='unclassified')[source]

Label every cell–cell contact by its two cells’ NLS subpopulation labels.

For each kind == "cell_cell" edge, each endpoint’s label is looked up in labels (cell_id -> label; a cell absent from the map takes unclassified) and the unordered-pair contact label "-".join(sorted([label_a, label_b])) is formed. The vocabulary is whatever labels holds — nothing here is hard-wired to positive/negative.

Border edges (kind == "border", cell_b == 0) are not contacts between two cells and are excluded. Fragments are not joined: a boundary split across several edge rows yields several labelled rows that share the same (frame, cell_a, cell_b) and therefore the same label; edge_id and length are carried through so a consumer can join or length-weight later.

Columns (column-major, all equal length, one row per cell–cell edge):

  • frame — the edge’s frame.

  • edge_id — the edge’s id within its frame.

  • cell_a / cell_b — the contacting cell ids (as stored, a < b).

  • label_a / label_b — each cell’s NLS label, or unclassified.

  • contact_label — sorted "label_a-label_b" pair.

  • homotypiclabel_a == label_b (True for two unclassified; gate on fully_classified if that matters).

  • fully_classified — both cells had a label in labels.

  • length — the edge length, carried through for weighting.

Returns empty (but typed) arrays when there are no cell–cell edges.

Return type:

dict[str, ndarray]

Parameters:
itasc.contact_analysis.contacts.signed_central_junction_lengths(analysis, *, pixel_size_um=None, labels=None)[source]

Signed central junction length per T1 event, per frame.

For each T1 event the central junction is the edge that flips from the losing cell pair (pre-transition) to the gaining pair (post-transition). Every frame in which an event’s losing edge exists contributes a negative sample (−length); every frame its gaining edge exists contributes a positive one (+length). The magnitude is the edge length; it crosses zero at the four-fold vertex, so pooled and inverted these reproduce the reference’s double-well potential without curated quads.

Fragmented contacts are joined first: the build splits a single cell-cell boundary into several edge rows (one per disconnected segment from _coordinate_segments), so the lengths of all rows sharing a (frame, cell-pair) are summed into one total junction length before signing. Otherwise each fragment would enter the landscape as its own (short) sample. This is the headless analogue of the v1 find_shared_boundary / order_boundary_pixels join that produced one length per junction.

All frames an edge exists are used (no ± window) — matching the reference, which histograms the whole movie.

Columns (column-major, all equal length):

  • t1_event_id — the event each sample belongs to.

  • frame — the frame the sample is read from.

  • signed_length±length, in µm when pixel_size_um is given else px.

  • role"losing" (negative) or "gaining" (positive).

  • contact_type — the event’s transition pair "<losing>→<gaining>" (e.g. "A-A→A-B"), where each side is the contact label (contact_labels.contact_label_for()) of that junction’s cell pair. "" when no labels map is given (the label-agnostic default). A single per-event label is used (not per-side), so both the negative losing lobe and the positive gaining lobe of an event share it and a grouped curve still spans L = 0 for the barrier.

labels maps cell_id -> label (an optional, caller-supplied per-cell classification); a cell absent from it is "unclassified".

Returns empty (but typed) arrays when there are no events or no matching edges; an event whose losing/gaining edges never appear in edges simply contributes nothing.

Return type:

dict[str, ndarray]

Parameters:
itasc.contact_analysis.contacts.cell_neighbor_counts(analysis)[source]

Per (frame, cell_id) adjacency degree — the headline neighbor count.

One row per cell present in the cells table for that frame, with n_neighbors its number of distinct cell_cell neighbors (0 for an isolated cell). This count is also the numerator for density.

Columns (column-major, equal length): frame, cell_id, n_neighbors (int).

Return type:

dict[str, ndarray]

Parameters:

analysis (PositionContactAnalysis)

itasc.contact_analysis.contacts.cell_density(frame_cells, *, fov_area_mm2)[source]

Per frame cell count and density = n_cells / fov_area_mm2.

Emits one label="all" row per frame that counts every cell in the frame. density is in cells/mm²; fov_area_mm2 is the user’s field-of-view area and is required (a positive number) — there is no silent image-area fallback.

frame_cells maps frame -> [cell_id, …] (the cell labels present in that frame), so this counts straight off the cell labels with no contacts dependency.

Columns: frame, label (str, always "all"), n_cells (int), density (float, cells/mm²).

Return type:

dict[str, ndarray]

Parameters:

Submodules

batch

Name-based batch discovery and headless contact-analysis builds.

build

contact_labels

Contact cell-type labels — propagate a per-cell label onto contacts.

neighborhood

Neighborhood & density derivations over the cell–cell contact graph.

reader

signed_contact_length

Signed contact length — the signed central junction-length reaction coordinate.