Skip to content

napari_ndev.measure #

Functions for measuring properties of labels.

Measure properties of labels in images using sci-kit image's regionprops. It includes utilities for handling label and intensity images, extracting information from ID strings, renaming intensity columns, and mapping treatment dictionaries to DataFrame ID columns.

Functions:

  • measure_regionprops : Measure properties of labels with sci-kit image regionprops.
  • group_and_agg_measurements : Count and aggregate measurements by grouping IDs from measurement results.

group_and_agg_measurements #

group_and_agg_measurements(
    df, grouping_cols="id", count_col="label", agg_cols=None, agg_funcs="mean"
)

Count and aggregate measurements by grouping IDs from measurement results.

Parameters:

  • df #

    (DataFrame) –

    The DataFrame with measurement properties, usually from measure_regionprops.

  • grouping_cols #

    (str or list of str, default: 'id' ) –

    The columns to group by. By default, just the image ID.

  • count_col #

    (str, default: 'label' ) –

    The column to count. By default, just the 'label' column.

  • agg_cols #

    (list of str or None, default: None ) –

    The columns to aggregate. By default, None.

  • agg_funcs #

    (str or list of str, default: 'mean' ) –

    The aggregating functions. By default, just the mean.

Returns:

  • DataFrame

    The DataFrame with grouped and aggregated measurements.

Source code in src/napari_ndev/measure.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
def group_and_agg_measurements(
    df: pd.DataFrame,
    grouping_cols: str | list[str] = 'id',
    count_col: str = 'label',
    agg_cols: list[str] | None = None,
    agg_funcs: str | list[str] = 'mean',
) -> pd.DataFrame:
    """
    Count and aggregate measurements by grouping IDs from measurement results.

    Parameters
    ----------
    df : pd.DataFrame
        The DataFrame with measurement properties, usually from measure_regionprops.
    grouping_cols : str or list of str, optional
        The columns to group by. By default, just the image ID.
    count_col : str, optional
        The column to count. By default, just the 'label' column.
    agg_cols : list of str or None, optional
        The columns to aggregate. By default, None.
    agg_funcs : str or list of str, optional
        The aggregating functions. By default, just the mean.

    Returns
    -------
    pd.DataFrame
        The DataFrame with grouped and aggregated measurements.

    """
    # get count data
    df_count = (
            df.copy().groupby(grouping_cols)
            .agg({count_col: 'count'}) # counts count_col
            .rename(columns={count_col: f'{count_col}_count'})
            .reset_index()
        )

    if agg_cols is None or agg_cols == []:
        return df_count

    # get aggregated data
    agg_cols = df[agg_cols]
    agg_dict = {col: agg_funcs for col in agg_cols}
    df_agg = (
            df.copy()
            .groupby(grouping_cols)  # sw
            .agg(agg_dict)
            .reset_index()
        )  # genereates a multi-index
        # collapse multi index and combine columns names with '_' sep
    df_agg.columns = [
            f'{col[0]}_{col[1]}' if col[1] else col[0]
            for col in df_agg.columns
        ]

    # insert label count column into df_agg after grouping columns
    insert_pos = 1 if isinstance(grouping_cols, str) else len(grouping_cols)
    df_agg.insert(insert_pos, 'label_count', df_count['label_count'])

    return df_agg

measure_regionprops #

measure_regionprops(
    label_images,
    label_names=None,
    intensity_images=None,
    intensity_names=None,
    properties=None,
    scale=(1, 1),
    id_string=None,
    id_regex_dict=None,
    tx_id=None,
    tx_dict=None,
    tx_n_well=None,
    save_data_path=None,
)

Measure properties of labels with sci-kit image regionprops.

Optionally give a list of intensity_images to measure intensity properties of labels (i.e. 'intensity_mean', 'intensity_min', 'intensity_max', 'intensity_std'). If no label or intensity names are given, the names are automatically generated as a string of the input variable name. Choose from a list of properties to measure: [ "label", "area", "area_convex", "bbox", "centroid", "eccentricity", "extent", "feret_diameter_max", "intensity_max", "intensity_mean", "intensity_min", "intensity_std", "num_pixels", "orientation", "perimeter", "solidity", ].

Parameters:

  • label_images #

    (list of ArrayLike or ArrayLike) –

    The label images.

  • label_names #

    (list of str or str or None, default: None ) –

    The names of the label images.

  • intensity_images #

    (list of ArrayLike or ArrayLike or None, default: None ) –

    The intensity images.

  • intensity_names #

    (list of str or str or None, default: None ) –

    The names of the intensity images.

  • properties #

    (list of str or None, default: None ) –

    The properties to measure.

  • scale #

    (tuple of float, default: (1, 1) ) –

    The scale for the measurements.

  • id_string #

    (str or None, default: None ) –

    The ID string.

  • id_regex_dict #

    (dict or None, default: None ) –

    The regex dictionary for extracting information from the ID string.

  • tx_id #

    (str or None, default: None ) –

    The treatment ID.

  • tx_dict #

    (dict or None, default: None ) –

    The treatment dictionary.

  • tx_n_well #

    (int or None, default: None ) –

    The number of wells in the plate.

  • save_data_path #

    (PathLike or None, default: None ) –

    The path to save the data.

Returns:

  • DataFrame

    The DataFrame with measured properties.

Source code in src/napari_ndev/measure.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def measure_regionprops(
    label_images: list[ArrayLike] | ArrayLike,
    label_names: list[str] | str | None = None,
    intensity_images: list[ArrayLike] | ArrayLike | None = None,
    intensity_names: list[str] | str | None = None,
    properties: list[str] | None = None,
    scale: tuple[float, float] | tuple[float, float, float] = (1, 1),
    id_string: str | None = None,
    id_regex_dict: dict | None = None,
    tx_id: str | None = None,
    tx_dict: dict | None = None,
    tx_n_well: int | None = None,
    save_data_path: PathLike = None,
) -> pd.DataFrame:
    """
    Measure properties of labels with sci-kit image regionprops.

    Optionally give a list of intensity_images to measure intensity properties
    of labels (i.e. 'intensity_mean', 'intensity_min', 'intensity_max',
    'intensity_std'). If no label or intensity names are given, the names are
    automatically generated as a string of the input variable name.
    Choose from a list of properties to measure: [
            "label",
            "area",
            "area_convex",
            "bbox",
            "centroid",
            "eccentricity",
            "extent",
            "feret_diameter_max",
            "intensity_max",
            "intensity_mean",
            "intensity_min",
            "intensity_std",
            "num_pixels",
            "orientation",
            "perimeter",
            "solidity",
        ].

    Parameters
    ----------
    label_images : list of ArrayLike or ArrayLike
        The label images.
    label_names : list of str or str or None, optional
        The names of the label images.
    intensity_images : list of ArrayLike or ArrayLike or None, optional
        The intensity images.
    intensity_names : list of str or str or None, optional
        The names of the intensity images.
    properties : list of str or None, optional
        The properties to measure.
    scale : tuple of float, optional
        The scale for the measurements.
    id_string : str or None, optional
        The ID string.
    id_regex_dict : dict or None, optional
        The regex dictionary for extracting information from the ID string.
    tx_id : str or None, optional
        The treatment ID.
    tx_dict : dict or None, optional
        The treatment dictionary.
    tx_n_well : int or None, optional
        The number of wells in the plate.
    save_data_path : PathLike or None, optional
        The path to save the data.

    Returns
    -------
    pd.DataFrame
        The DataFrame with measured properties.

    """
    from skimage import measure

    if properties is None:
        properties = ['area']
    measure_dict = _generate_measure_dict(
        label_images, label_names, intensity_images, intensity_names
    )

    if intensity_images is not None:
        if len(measure_dict['intensity_images']) == 1:
            intensity_stack = measure_dict['intensity_images'][0]
        else:
            intensity_stack = np.stack(
                measure_dict['intensity_images'], axis=-1
            )
    else:
        intensity_stack = None

    measure_df_list = []

    for label_idx, label_image in enumerate(measure_dict['label_images']):
        measure_props = measure.regionprops_table(
            label_image=label_image,
            intensity_image=intensity_stack,
            properties=properties,
            spacing=scale,
        )

        measure_df = pd.DataFrame(measure_props)
        measure_df.insert(0, 'label_name', measure_dict['label_names'][label_idx])
        measure_df_list.append(measure_df)

    if len(measure_df_list) > 1:
        measure_df = pd.concat(measure_df_list, ignore_index=True)

    if intensity_names is not None:
        measure_df = _rename_intensity_columns(
            measure_df, measure_dict['intensity_names']
        )

    measure_df.insert(1, 'id', id_string)

    if id_regex_dict is not None:
        id_dict = _extract_info_from_id_string(id_string, id_regex_dict)
        for key, value in id_dict.items():
            measure_df.insert(2, key, value)

    if tx_id is not None and tx_dict is not None:
        _map_tx_dict_to_df_id_col(tx_dict, tx_n_well, measure_df, tx_id)

    if save_data_path is not None:
        measure_df.to_csv(save_data_path, index=False)

    return measure_df