Skip to main content

functions

Pure biomarker tabulation logic + Granite biomarker constants.

Ported verbatim from the v8 Granite protocol (federated/protocols/ophthalmology/generic_biomarker_protocol_granite.py): the per-metric-dict → DataFrame converters, the drusen / per-label segmentation-area derivations, and the biomarker_groups/biomarker_names COLUMN filter. The merge step itself now goes through the shared bitfount.steps.data_utils.assemble_scan_frame (also used by criteria_matching), called directly from v1/task.py.

The filter operates on the TECHNICAL (internal) column names — display renaming via GRANITE_CSV_RENAME_COLS is the downstream csv_report step's job, NOT this one. self.biomarker_groups / self.biomarker_names became explicit parameters and ProtocolError became a plain ValueError (fail-loud on unknown group/name or an empty final selection).

Module

Functions

add_drusen_columns

def add_drusen_columns(merged_df: pd.DataFrame)> pandas.core.frame.DataFrame:

Derive drusen presence + aggregate columns from the per-label drusen areas.

Reads the three drusen <label>_area columns (hard_drusen_area / soft_drusen_area / confluent_drusen_area, produced by add_segmentation_area_columns) and derives _DRUSEN_DERIVED_COLUMNS: per-type presence flags, an any-present flag, a type count, and the largest / combined areas. drusen_largest_area is the largest of the three per-subtype TOTAL areas (e.g. all hard drusen vs. all soft drusen combined); it is not the largest individual lesion and cannot answer "is there at least one large druse". The lesion_calculation step's drusen_largest_lesion_area is the per-lesion figure. Run add_segmentation_area_columns first — when any drusen area column is absent (no segmentation data), every derived column is pd.NA.

Arguments

  • merged_df: The merged biomarker DataFrame carrying the drusen <label>_area columns.

Returns The same DataFrame with the derived drusen columns appended in-place.

add_segmentation_area_columns

def add_segmentation_area_columns(merged_df: pd.DataFrame)> pandas.core.frame.DataFrame:

Flatten the raw segmentation area mapping into per-label columns.

The sole producer of the <label>_area columns: one per AREA_ELIGIBLE_SEGMENTATION_LABELS entry, including the three drusen labels (add_drusen_columns then derives the drusen presence/aggregate columns from these). Fluid labels are excluded: fluid is quantified by volume, so a projected area for them is meaningless. When the segmentation_areas column is absent (no segmentation data), every <label>_area column is pd.NA.

Arguments

  • merged_df: The merged biomarker DataFrame, potentially carrying a segmentation_areas column of per-file area dicts.

backfill_metrics

def backfill_metrics(    metrics: Mapping[str, _MetricT], filenames: list[str],)> dict[str, typing.Optional[~_MetricT]]:

Ensure every selected filename has an entry in a metric mapping.

Adds any filenames missing from metrics with a None value (which the downstream convert_*_to_df helpers turn into an all-NA row) so a scan that a calculation skipped still appears in the tabulation. Existing values — including str/None error sentinels — are left untouched, and keys not in filenames are preserved rather than dropped.

build_cst_df_from_cache

def build_cst_df_from_cache(    cache_df: pd.DataFrame, filenames: list[str],)> pandas.core.frame.DataFrame:

Build the (canonically-named) CST metrics DataFrame from the cache frame.

Reconstructs CSTMetrics from each cache row's metrics_json blob, backfills filenames absent from the cache with None (an all-NA row), and delegates to convert_metrics_to_df + apply_canonical_scan_metric_names for the CST/GCC shared-descriptor rename (cst_*).

Arguments

  • cache_df: The cst_calculation step's cache partition frame.
  • filenames: The selected filenames (drives the backfill).

Returns The CST metrics DataFrame, filename column first.

build_fluid_df_from_cache

def build_fluid_df_from_cache(    cache_df: pd.DataFrame, filenames: list[str],)> pandas.core.frame.DataFrame:

Build the fluid metrics DataFrame from the fluid_calculation cache frame.

Reconstructs FluidVolumeMetrics from each cache row's metrics_json blob, backfills filenames absent from the cache with None (an all-NA row), and delegates to convert_metrics_to_df + apply_canonical_scan_metric_names: every scalar field is prefixed with FLUID_PREFIX, the raw segmentation_volumes dict is flattened into the unprefixed FLUID_VOLUME_COLUMNS and then dropped (not surfaced as a column).

Arguments

  • cache_df: The fluid_calculation step's cache partition frame.
  • filenames: The selected filenames (drives the backfill).

Returns The fluid metrics DataFrame, filename column first.

build_ga_df_from_cache

def build_ga_df_from_cache(    cache_df: pd.DataFrame, filenames: list[str],)> pandas.core.frame.DataFrame:

Build the GA metrics DataFrame from the ga_calculation cache frame.

Reconstructs GAMetricsWithFovea from each cache row's metrics_json blob, backfills filenames absent from the cache with None (an all-NA row), and delegates to convert_ga_metrics_to_df for the flatten: the scalar + segmentation_areas fields (retained raw — the downstream add_segmentation_area_columns / add_drusen_columns derive from it) plus a max_<pathology>_probability column per Granite pathology (NaN when a pathology is absent from a file's max_pathology_probabilities map, or the whole row is backfilled/uncomputable).

Arguments

  • cache_df: The ga_calculation step's cache partition frame.
  • filenames: The selected filenames (drives the backfill).

Returns The GA metrics DataFrame, filename column first.

build_gcc_df_from_cache

def build_gcc_df_from_cache(    cache_df: pd.DataFrame, filenames: list[str],)> pandas.core.frame.DataFrame:

Build the (canonically-named) GCC metrics DataFrame from the cache frame.

Reconstructs GCCMetrics from each cache row's metrics_json blob, backfills filenames absent from the cache with None (an all-NA row), and delegates to convert_metrics_to_df + apply_canonical_scan_metric_names for the CST/GCC shared-descriptor rename (gcc_*).

Arguments

  • cache_df: The gcc_calculation step's cache partition frame.
  • filenames: The selected filenames (drives the backfill).

Returns The GCC metrics DataFrame, filename column first.

build_lesion_df_from_cache

def build_lesion_df_from_cache(    cache_df: pd.DataFrame, filenames: list[str],)> pandas.core.frame.DataFrame:

Build the area metrics DataFrame from the lesion_calculation cache frame.

Reconstructs LesionMetrics from each cache row's metrics_json blob, backfills filenames absent from the cache with None (an all-NA row), and delegates to convert_metrics_to_df. No canonical rename applies: the group columns are already flat and group-prefixed (LesionMetrics.to_record), because the group set is config-defined and so cannot be spelled out in a static rename map the way fluid's is.

Arguments

  • cache_df: The lesion_calculation step's cache partition frame.
  • filenames: The selected filenames (drives the backfill).

Returns The area metrics DataFrame, filename column first.

convert_fluid_metrics_to_df

def convert_fluid_metrics_to_df(    fluid_metrics: Mapping[str, FluidVolumeMetrics | str | None],)> pandas.core.frame.DataFrame:

Convert a filename→FluidVolumeMetrics mapping into a flat DataFrame.

Each key becomes one row. Metric field names are prefixed with FLUID_PREFIX (fluid_) so they do not collide with columns from other biomarker DataFrames during the outer merge. When a file's metrics are None (or a str error/missing-data sentinel), the corresponding columns are filled with pd.NA.

Arguments

  • fluid_metrics: Mapping of filename → FluidVolumeMetrics (or None / a str sentinel for files with no fluid data).

Returns DataFrame with one row per file and prefixed fluid metric columns. If the mapping is empty, returns an empty DataFrame with only the filename column.

get_biomarker_group_columns

def get_biomarker_group_columns(df: pd.DataFrame)> dict[str, set[str]]:

Build a mapping from biomarker group names to their available columns.

Inspects the columns present in df and partitions them into the biomarker groups (ga_metrics, fluid_metrics, cst_metrics, gcc_metrics, classification_metrics, drusen_metrics, segmentation_areas, lesion_metrics). Only columns that actually exist in df are included, so groups may be empty if the corresponding calculation step failed or was skipped.

Arguments

  • df: The merged biomarker DataFrame after all calculation steps.

Returns Dict mapping each group name to the set of column names belonging to that group that are present in df.

lesion_rename_columns

def lesion_rename_columns(*, roi_radius_mm: float | None)> dict[str, str]:

Build the technical-to-display rename map for every lesion group column.

Emits both the plain <group>_<suffix> header and the ROI-qualified <group>_roi_<suffix> header for every group in _LESION_GROUP_DISPLAY_NAMES and every suffix in _LESION_GROUP_METRIC_DISPLAY_NAMES. The ROI-qualified header names the region measured, so a whole-scan measurement and a central-region one are not indistinguishable in the report. roi_radius_mm is a runtime value (a per-step config field), so this map cannot be a static module-level constant the way GRANITE_CSV_RENAME_COLS is.

Arguments

  • roi_radius_mm: Radius in mm of the configured region of interest, or None when no ROI is configured for the step. Named in every ROI-qualified header.

Returns Technical column name to display header, for every group/suffix pair, both plain and ROI-qualified.

rename_cst_metric_columns

def rename_cst_metric_columns(    cst_metrics_df: pd.DataFrame,)> pandas.core.frame.DataFrame:

Expose explicit CST-prefixed names for shared thickness fields.

rename_gcc_metric_columns

def rename_gcc_metric_columns(    gcc_metrics_df: pd.DataFrame,)> pandas.core.frame.DataFrame:

Avoid GCC/CST collisions for shared thickness metric field names.

select_biomarker_columns

def select_biomarker_columns(    df: pd.DataFrame, biomarker_groups: list[str], biomarker_names: list[str],)> pandas.core.frame.DataFrame:

Filter the merged DataFrame to only the requested biomarker columns.

Resolves the biomarker_groups and biomarker_names selection into a concrete set of columns. When neither is set (both empty), all available biomarker columns are included. Fails loudly with ValueError on an unknown group, an unknown column name, or an empty final selection (never silently broadens). The filename column is always retained and the source column order preserved.

Arguments

  • df: The merged biomarker DataFrame containing all computed columns.
  • biomarker_groups: Group names to include (empty ⇒ all groups).
  • biomarker_names: Individual column names to include (empty ⇒ none extra).

Returns A copy of df containing only the selected biomarker columns, in the original column order.