Source code for phenotypic.measure._measure_grid_spread

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from phenotypic._core._grid_image import GridImage
from phenotypic.abc_ import GridMeasureFeatures
from ..tools_.measurement_info_ import GRID_SPREAD

import pandas as pd
import numpy as np
from scipy.spatial import distance_matrix
from phenotypic.tools_.measurement_info_ import BBOX, GRID


[docs] class MeasureGridSpread(GridMeasureFeatures): """Quantify within-well colony dispersion using pairwise centroid distances. Compute the sum of squared pairwise Euclidean distances between all colony centroids in each grid section. High values indicate multiple dispersed objects within a single well -- a sign of over-segmentation, fragmented growth, or invasive spreading. Returns: pd.DataFrame: Section-level statistics sorted by spread (descending) with columns: - count: number of colonies detected in the section. - ObjectSpread: sum of squared pairwise Euclidean distances between colony centroids in the section. Best For: - Detecting over-segmented wells where multiple objects were found instead of a single cohesive colony. - Identifying invasive or spreading growth that extends beyond the designated grid position. - Flagging wells with questionable data quality for manual review or exclusion from downstream analysis. Consider Also: - :class:`MeasureGridSpatial` for between-well neighbor distances rather than within-well dispersion. - :class:`MeasureGridLinRegStats` for positional accuracy metrics based on linear regression. - :class:`MeasureBounds` for raw centroid positions per colony. See Also: :doc:`/tutorials/notebooks/07_measuring_and_exporting` for a walkthrough of grid-level measurements. """ _measurement_info_class = GRID_SPREAD def _operate(self, image: GridImage) -> pd.DataFrame: gs_table = image.grid.info() gs_counts = pd.DataFrame( gs_table.loc[:, str(GRID.ROW_MAJOR_IDX)].value_counts()) obj_spread = [] for gs_bindex in gs_counts.index: curr_gs_subtable = gs_table.loc[ gs_table.loc[:, str(GRID.ROW_MAJOR_IDX)] == gs_bindex, : ] x_vector = curr_gs_subtable.loc[:, str(BBOX.CENTER_CC)] y_vector = curr_gs_subtable.loc[:, str(BBOX.CENTER_RR)] obj_vector = np.array(list(zip(x_vector, y_vector))) gs_distance_matrix = distance_matrix(x=obj_vector, y=obj_vector, p=2) obj_spread.append(np.sum(np.unique(gs_distance_matrix) ** 2)) gs_counts.insert(loc=1, column=str(GRID_SPREAD.OBJECT_SPREAD), value=pd.Series(obj_spread)) gs_counts.sort_values(by=str(GRID_SPREAD.OBJECT_SPREAD), ascending=False, inplace=True) return gs_counts
MeasureGridSpread.__doc__ = GRID_SPREAD.append_rst_to_doc(MeasureGridSpread)