PhenoMe API Reference
Tier: Public API
See also: Getting started · Concepts · Visualization · Distances · Model wrappers
Class PhenoMe
Section titled “Class PhenoMe”Main class for phenotyping analysis using deep learning embeddings.
Provides: compute_properties, property_stats_by_group; compute_clustering, detect_outliers, find_prototypes; compute_reference_distances; correct_batches; plot_pca, plot_tsne, plot_umap, and related methods.
Embedding lifecycle:
- Eager: When no checkpoint is used, embeddings live in
results.embeddings(np.ndarray). - Lazy: When a checkpoint is active (
self._db),results.embeddingsisNone; embeddings are read on demand viaget_embeddings()from the HDF5 file. - Temporal: Rows from
process_temporal_images()useself._temporal_embeddingswhile_dbis open;get_embeddings()merges them with checkpoint rows by index. Aftercheckpoint_context()exits with temporal data present, the merged array is copied toresults.embeddingsand the temporal buffer is cleared.
Args:
device: Optional torch.device for GPU-accelerated analysis operations. Recommended for faster computation. If None, operations run on CPU.seed: Optional random seed for reproducibility (default: None). When set, used for all random operations (determinism, clustering, t-SNE, UMAP, mutual information, random sampling in reports). If None, no seeds are set anywhere.use_gpu_for_dr: If True and device is CUDA, uses TorchDR for GPU-accelerated dimensionality reduction (PCA, t-SNE, UMAP). If False (default), always use sklearn/umap-learn on CPU.
PhenoMe( device: torch.device | str | None = None, seed: int | None = None, use_gpu_for_dr: bool = False)Data ingestion and embedding extraction
Section titled “Data ingestion and embedding extraction”find_files
PhenoMe.find_files( self, image_dir: str, mask_dir: str | None = None, extensions: list[str] | None = None, metadata_fn: collections.abc.Callable[[str], dict[str, typing.Any]] | phenome.metadata.base.MetadataBase | None = None, on_missing_metadata: str = 'drop', mask_filename_column: str | None = None, mask_extensions: list[str] | None = None, channel_names: list[str] | None = None) -> DataFrameDiscover image and mask files from directories, extract metadata, and cache internally.
Recursively scans image_dir for image files and optionally aligns them with masks
from mask_dir. Extracts per-file metadata using metadata_fn and stores the result
as the internal file DataFrame used by process_images(), compute_properties(),
and inspect_data().
Args:
image_dir(str): Root directory or glob pattern for image files. Scanned recursively. Example: “path/to/images/” or “path/to/**/*.tif”.mask_dir(str or None): Root directory for corresponding mask files. If provided, adds a ‘mask_path’ column to the output DataFrame. Default: None (no masks).extensions(list of str or None): File extensions to include (e.g., [“.tif”, “.png”, “.jpg”]). If None, defaults to common image formats. Default: None.metadata_fn(callable, MetadataBase, or None): Function or object to extract metadata from file paths. Signature:fn(file_path: str) -> dict[str, Any]. Examples:DefaultMetadata(): Simple metadata.PathTemplateMetadata(template="..."): Extract from path pattern.lambda p: {'batch': p.split('/')[-2]}: Custom function. If None, no metadata extracted. Default: None.
on_missing_metadata(str): How to handle files with missing metadata. One of:- ‘drop’: Remove rows with incomplete metadata.
- ‘keep’: Include rows with NaN metadata. Default: ‘drop’.
mask_filename_column(str or None): Metadata column name used to locate mask files. If set, masks are located using this column value. Useful for custom mask naming. Default: None (standard alignment logic).mask_extensions(list of str or None): Extensions to try when exact mask filename fails (e.g., [“.png”, “.tif”]). Default: None (exact filename required).channel_names(list of str or None): Optional labels for image channels, in channel-axis order (same order as list entries infile_pathfor multi-file samples). When set, multi-channel property columns use these labels as suffixes (e.g.intensity_entropy_DAPIinstead ofintensity_entropy_ch0). Names must be non-empty, unique (case-insensitive), and contain no whitespace. Default: None (integer_ch{idx}suffixes).
Returns:
pd.DataFrame: File discovery DataFrame with columns:
- ‘file_path’: (str or list of str) Image file path(s).
- ‘mask_path’: (str, if mask_dir set) Corresponding mask path.
- Metadata columns: Custom columns from
metadata_fnif provided. Shape: (N, 2+M) where N is number of images, M is metadata columns.
Raises:
FileNotFoundError: Ifimage_dirdoes not exist.ValueError: If all files are dropped due toon_missing_metadata='drop', or ifchannel_namesis invalid.RuntimeError: If metadata extraction fails or masks cannot be resolved.
Example:
Discover images with metadata:
>>> pm = PhenoMe()>>> df = pm.find_files(... "data/images/",... metadata_fn=PathTemplateMetadata(template="batch_{batch}/sample_{id}"),... extensions=[".tif", ".png"]... )>>> print(df.shape)(150, 4) # 150 images, 4 columns: file_path, batch, id, [others]
With masks:
>>> df = pm.find_files(... image_dir="data/images/",... mask_dir="data/masks/",... extensions=[".tif"]... )>>> print(df.columns)Index(['file_path', 'mask_path'], dtype='object')
Named channels for property suffixes:
>>> df = pm.find_files(... "data/images/",... channel_names=["DAPI", "GFP"],... )See Also:
set_file_df: Manually provide a file DataFrame.process_images: Process discovered images.
set_file_df
PhenoMe.set_file_df( self, file_df: pandas.DataFrame, channel_names: list[str] | None = None) -> DataFrameSet the internal file DataFrame used for processing.
Validates that file_df has a ‘file_path’ column and optionally ‘mask_path’. Stores a normalized copy for use by process_images, compute_properties, inspect_data, and related methods.
Args:
file_df: DataFrame with ‘file_path’ column (str or list of str per row). May include ‘mask_path’ and metadata columns.channel_names: Optional labels for image channels, in channel-axis order (same order as list entries infile_pathfor multi-file samples). When set, multi-channel property columns use these labels as suffixes (e.g.intensity_entropy_DAPIinstead ofintensity_entropy_ch0). Default: None (integer_ch{idx}suffixes).
Returns:
The normalized DataFrame that was stored.
process_images
PhenoMe.process_images( self, model_wrapper: phenome.utils.model_wrapper.ModelWrapper, batch_size: int = 32, num_workers: int = 4, filters: dict[str, list[typing.Any]] | None = None, exclude: dict[str, list[typing.Any]] | None = None, channel_mode: Literal['split', 'combined'] = 'split', channels: list[int] | None = None, preprocessing_fn: collections.abc.Callable[[numpy.ndarray], numpy.ndarray] | None = None, custom_transformations: typing.Any | None = None, preprocessing_description: str | None = None, transformations_description: str | None = None, append: bool = False, resize_size: int | None = 224, pad_size: int | None = None, checkpoint_path: str | None = None, force_rgb: bool = True, l2_normalize_channels: bool = True, save_every: int = 5, lazy_checkpoint: bool = True, force_reprocess: bool = False, progress_callback: collections.abc.Callable[[int, int, str], None] | None = None) -> NoneExtract embeddings from images using a pretrained or custom model.
Reads images from the file DataFrame (set via find_files() or set_file_df()),
processes them through the model in batches, and stores embeddings and metadata
in self.results. Supports filtering, channel selection, custom preprocessing,
and checkpointing for resumable processing.
Embedding storage:
- Eager (default): Embeddings stored in
results.embeddings(ndarray). - Lazy (with checkpoint): Embeddings stored in HDF5;
results.embeddingsis None and embeddings are loaded on-demand viaget_embeddings(). - Temporal (incremental): New images added to existing checkpoint via
process_temporal_images().
Default processing order (when custom_transformations is None):
- Load image (native dtype)
- Optional
preprocessing_fn - Channel selection /
channel_mode/force_rgb - TypeMaxNorm intensity scaling (identity if max <= 1)
- Optional pad, then resize (default 224)
- ImageNet mean/std normalize
- Vision model -> embedding
- Split mode: L2-normalize each channel embedding (default True)
A concise processing pipeline is logged at INFO. Inspect
get_processing_pipeline() or print_processing_pipeline().
Custom custom_transformations replace steps 4-6.
Args:
model_wrapper(ModelWrapper): Model instance (e.g., fromload_dinov2_model()). Must implement_get_embeddings(tensor: torch.Tensor) -> torch.Tensor. For non-PyTorch models (Keras, TensorFlow, etc.), see the Custom Model Wrapper guide.batch_size(int): Batch size for processing. Default: 32. Larger batches are faster but use more GPU memory.num_workers(int): Number of loader processes. In Jupyter, automatically reduced to 0 to avoid multiprocessing issues. Default: 4.filters(dict or None): Include only rows matching criteria. Format:{column: [value1, value2, ...]}. Default: None (no filtering).exclude(dict or None): Exclude rows matching any criterion (OR across fields). Same value format asfilters. Default: None.channel_mode(str): How to handle multi-channel images. One of:- ‘split’: Process each channel separately (default).
- ‘combined’: Process all channels as RGB or grayscale.
channels(list of int or None): Specific channel indices to use. If None, uses all available. Default: None.preprocessing_fn(callable or None): Custom preprocessing function applied to each image before the model. Signature:fn(image: ndarray) -> ndarray. Example: normalization, contrast adjustment, etc. Default: None.custom_transformations(transforms object or None): PyTorch transforms to apply before the model (e.g., resize, normalize). If None, the default order is TypeMaxNorm, optional pad, optional resize, then ImageNet Normalize. Default: None.preprocessing_description(str or None): Optional plain-language note forpreprocessing_fn. Shown in the INFO processing pipeline log and onget_processing_pipeline(). Internals are never inspected. Recommended when using custom preprocessing so the log describes what the function does. Default: None.transformations_description(str or None): Optional plain-language note forcustom_transformations. Shown in the INFO processing pipeline log and onget_processing_pipeline(). Internals are never inspected. Default: None.append(bool): If True, append to existing embeddings inresults.embeddings. If False (default), replace. Default: False.resize_size(int or None): Image resize dimension (square). Default: 224. Set to None to skip resizing.pad_size(int or None): Padding size (square). Default: None (no padding).checkpoint_path(str or None): HDF5 file path for lazy storage and resumable processing. If file exists, processing resumes from last checkpoint. If None, embeddings stored in-memory. Default: None.force_rgb(bool): If True, convert grayscale to RGB before model. Default: True.l2_normalize_channels(bool): Inchannel_mode='split', L2-normalize each channel embedding before concatenation so channels contribute equally. Set False to preserve raw channel magnitudes (one channel may dominate). Ignored in combined mode. Default: True.save_every(int): Save checkpoint every N batches (when using checkpoint_path). Default: 5.lazy_checkpoint(bool): If True and checkpoint_path is set, enable lazy loading (embeddings not kept in-memory). If False, load all embeddings after processing. Default: True.force_reprocess(bool): If True andcheckpoint_pathalready exists, delete that file and process all requested images from scratch (no resume). Default: False.progress_callback(callable or None): Optional(current, total, desc) -> Nonehook for GUI/notebook progress bars. Invoked after each batch. When set, the defaulttqdmbar is disabled. Default: None.
Returns:
None. Modifies self.results in-place with embeddings, metadata, and paths.
Raises:
FileNotFoundError: If file_df not set (callfind_files()orset_file_df()first).ValueError: If filters/exclude reference non-existent columns or produce no data.RuntimeError: If model inference fails or checkpoint is corrupted.IOError: If checkpoint file cannot be created or read.
Example:
Process images with DINOv2 model:
>>> from phenome import PhenoMe, load_dinov2_model>>> pm = PhenoMe(device="cuda")>>> pm.find_files("images/")>>> model = load_dinov2_model("dinov2_vitb14")>>> pm.process_images(model, batch_size=64, resize_size=224)>>> print(pm.results.embeddings.shape)(1000, 768)
Optional description for custom preprocessing (appears in the INFOprocessing pipeline log even though the callable itself is opaque):
>>> def enhance_contrast(image):... p1, p99 = np.percentile(image, (1, 99))... return np.clip(image, p1, p99)>>> pm.process_images(... model,... preprocessing_fn=enhance_contrast,... preprocessing_description="clip 1st/99th percentiles",... )
With filtering and checkpoint:
>>> pm.process_images(... model,... filters={"batch": ["batch_1", "batch_2"]},... checkpoint_path="embeddings.h5",... lazy_checkpoint=True... )
With a Jupyter progress bar:
>>> from ipywidgets import IntProgress, Label, VBox>>> from IPython.display import display>>> bar, lab = IntProgress(max=1), Label()>>> display(VBox([lab, bar]))>>> def on_progress(current, total, desc):... bar.max = max(total, 1)... bar.value = current... lab.value = f"{desc}: {current}/{total}">>> pm.process_images(model, progress_callback=on_progress)See Also:
find_files: Discover and organize image files.process_temporal_images: Incrementally add images to existing checkpoint.get_embeddings: Retrieve embeddings (handles lazy loading).compute_properties: Extract morphological properties from embeddings.get_processing_pipeline: Structured ordered processing pipeline for this run.print_processing_pipeline: Print the same text as the INFO log.
process_temporal_images
PhenoMe.process_temporal_images( self, model_wrapper: phenome.utils.model_wrapper.ModelWrapper, files: str | list[str] | pandas.DataFrame, batch_size: int = 32, num_workers: int = 4, channel_mode: Optional[Literal['split', 'combined']] = None, channels: list[int] | None = None, preprocessing_fn: collections.abc.Callable[[numpy.ndarray], numpy.ndarray] | None = None, custom_transformations: typing.Any | None = None, preprocessing_description: str | None = None, transformations_description: str | None = None, resize_size: int | None = None, pad_size: int | None = None, force_rgb: bool | None = None, l2_normalize_channels: bool | None = None, extensions: list[str] | None = None) -> NoneProcess new images in-memory (temporary) and append to the current session.
Use this to explore additional images (e.g., from a new condition or replicate) without re-running process_images. Temporal images are not persisted to checkpoint; visualize or export before calling clear_temporal_data() or reset().
Requires process_images() to have been run first. Processing parameters
(channel_mode, channels, resize_size, etc.) are inherited from the base run
when not specified. Temporal rows get metadata[‘source’] = ‘NEW’.
Optional preprocessing_description / transformations_description
are recorded on the session-only temporal pipeline (same optional notes as
process_images; they appear in the INFO processing pipeline log).
Default processing order matches process_images (load, optional
preprocess, channels/RGB, TypeMaxNorm, pad/resize, ImageNet, model,
split L2). A concise processing pipeline is logged at INFO.
inspect_data
PhenoMe.inspect_data( self) -> DataFrameInspect image and mask dimensions, shapes, dtypes, and data ranges.
Delegates to FileDiscovery. Uses stored dtypes (not the float32 cast
from read_image). Requires file_df from set_file_df or
find_files.
clear_temporal_data
PhenoMe.clear_temporal_data( self) -> intRemove all temporal images (metadata[‘source’] == ‘NEW’) from the session.
Returns:
int: Number of temporal rows removed.
Accessors and inspection
Section titled “Accessors and inspection”get_embeddings
PhenoMe.get_embeddings( self, indices: int | list[int] | numpy.ndarray | None = None) -> numpy.ndarray | NoneReturn embeddings for the given row indices.
When a checkpoint / HDF5 database is active (self._db is not None),
only the requested rows are read from disk — the core of the lazy-loading
strategy. When temporal embeddings exist (from process_temporal_images),
rows with index >= self._temporal_start_idx are read from
self._temporal_embeddings; earlier rows come from the checkpoint.
When no database is active, returns results.embeddings (optionally sliced).
If that array holds only checkpoint rows (length _temporal_start_idx) and
temporal embeddings are still in memory, they are appended to form a full
(n_images, D) array. If results.embeddings already has n_images rows
(e.g. materialized on checkpoint_context exit), temporal data is not appended
again.
Args:
indices: Optional row index or sequence of row indices (0-based). A singleintreturns shape(1, D). If None, returns all embeddings.
Returns:
np.ndarray shape (len(indices), D) float32, or None if no
embeddings are available.
get_image_info
PhenoMe.get_image_info( self, idx: int, distance_results: pandas.DataFrame | None = None) -> dictReturn metadata, properties, and optional distance for image idx.
Args:
idx: Image index (0 to n_images-1).distance_results: Optional DataFrame from compute_reference_distances.
Returns:
dict: Keys image_index, image_name, image_path, metadata keys, property keys, distance (if distance_results provided), is_reference (if applicable).
get_available_metadata_keys
PhenoMe.get_available_metadata_keys( self) -> listReturn sorted metadata keys.
get_available_property_keys
PhenoMeProperties.get_available_property_keys( self) -> listReturn sorted list of property keys stored in results.
has_embeddings
Property on PhenoMe
Return True if embedding data is available (lazy or eager).
A properties-only checkpoint has n_committed > 0 but no embedding
dataset; we additionally require embedding_dim to be a positive int.
embedding_dim
Property on PhenoMe
Return the embedding dimensionality, or 0 if unavailable.
get_processing_pipeline
PhenoMe.get_processing_pipeline( self) -> dict[str, Any] | NoneReturn the session processing pipeline, or None if none was recorded.
The dict has version, base, and optional temporal and
properties. After load_results, embedding and property steps
come from /processing_pipeline; temporal exists only in the current
session.
See Also:
print_processing_pipeline: Print the concise pipeline text.
print_processing_pipeline
PhenoMe.print_processing_pipeline( self) -> NonePrint the ordered processing pipeline for this session.
Uses the same text as the INFO log during process_images and
compute_properties. After load_results, prints the steps
stored in /processing_pipeline (embeddings and properties).
See Also:
get_processing_pipeline: Structured dict form of the same record.
Data lifecycle and export
Section titled “Data lifecycle and export”reset
PhenoMe.reset( self, verbose: bool = False, clear_file_df: bool = False) -> NoneReset all stored data to a clean state.
Closes any open HDF5 database handle before clearing results. By default preserves _file_df so compute_properties and inspect_data continue to work after process_images. Set clear_file_df=True for a full reset (e.g. when switching to a completely new dataset).
reset_properties
PhenoMeProperties.reset_properties( self, verbose: bool = False) -> NoneReset only the computed properties.
Args:
verbose: If True, logs a confirmation message.
transfer_metadata_to_properties
PhenoMeProperties.transfer_metadata_to_properties( self, columns: str | list[str]) -> NoneTransfer specified metadata columns to properties.
Args:
columns: A single metadata column name or a list of names to transfer.
save_results
PhenoMe.save_results( self, path: str | None = None, compression: str = 'gzip') -> NoneSave results to HDF5 (atomic write or in-place flush).
When a checkpoint database is already active (self._db is set),
the data is already on disk; this method flushes any uncommitted
buffers and logs the existing path. When no database is active,
writes self.results to a new HDF5 file using a temporary-file +
atomic rename for crash safety.
For full reproducibility, also call export_experiment_config to save seed, use_gpu_for_dr, reference_filters, and model name (not stored in the checkpoint).
Args:
path: Explicit output path.compression: HDF5 compression algorithm (used only for new files).
load_results
PhenoMe.load_results( self, path: str | None = None, lazy_checkpoint: bool = True) -> NoneLoad and use an existing results/checkpoint file.
Metadata and properties are loaded immediately. Embeddings are either loaded into RAM (lazy_checkpoint=False) or accessed on-demand from disk (lazy_checkpoint=True).
Uses the internally stored file_df (from find_files or set_file_df) to resolve paths so the checkpoint works on this machine. Call find_files or set_file_df first.
For separate embeddings and properties checkpoints, prefer load_embeddings then load_properties.
Args:
path: Path to .h5 or .hdf5 file.lazy_checkpoint: If True (default), keep the checkpoint file open. If False, load all data into RAM and close the file.
Raises:
FileNotFoundError: If file does not exist.ValueError: If file format is not recognised as HDF5.RuntimeError: If file_df is not available (call find_files or set_file_df first).ConcurrentCheckpointAccessError: If the checkpoint is already open in another notebook or process (only one instance can access it at a time).
checkpoint_context
PhenoMe.checkpoint_context( self, path: str | None = None, lazy_checkpoint: bool = True) -> GeneratorContext manager that loads a checkpoint and guarantees it is closed on exit.
More reliable than relying on del for cleanup. Use when you need to ensure the HDF5 file handle is released (e.g. before moving or deleting the file, or when opening multiple checkpoints in sequence).
Embeddings: While the context is open, lazy embeddings are read from the
HDF5 file via get_embeddings(). On exit, if temporal embeddings were loaded
in memory (process_temporal_images while the checkpoint was open), the merged
full embedding matrix is copied to results.embeddings and
self._temporal_embeddings is cleared. Otherwise results.embeddings may
stay None; call load_results(..., lazy_checkpoint=True) or enter another
checkpoint_context before using get_embeddings() again.
Args:
path: Checkpoint HDF5 path. If None, uses the path from the lastload_results/process_imagessession when available.lazy_checkpoint: If True, keep the HDF5 file open for on-demand reads inside the context. Default: True.
Example:
pheno.find_files("path/to/images")with pheno.checkpoint_context("results.h5") as p: p.plot_pca(color_by="condition")# Checkpoint closed hereexport_experiment_config
PhenoMe.export_experiment_config( self, path: str, reference_filters: dict[str, typing.Any] | None = None, model_name: str | None = None, checkpoint_path: str | None = None, **extra: Any) -> NoneExport experiment configuration for reproducibility.
Writes a JSON file with init parameters, environment, processing
params, and silently recorded method steps. Call after the analysis
you want to document. Optional model_name / reference_filters
override values already captured from process_images /
compute_reference_distances.
Args:
path: Output path for config.json.reference_filters: Optional dict used for compute_reference_distances (e.g. {‘condition’: ‘Control’}). Include for full traceability.model_name: Optional model identifier (e.g. ‘dinov2_vitb14_reg’).checkpoint_path: Optional checkpoint path used during processing (if not provided and a checkpoint is open, uses self._db.path). **extra: Additional key-value pairs to include in the config.
export_methods_markdown
PhenoMe.export_methods_markdown( self, path: str) -> NoneWrite a markdown methods summary from the silent run log.
Opt-in companion to export_experiment_config for paper supplements. Does nothing unless this method is called.
export_dataset_table
PhenoMe.export_dataset_table( self, output_path: str | None = None, dist_results: pandas.DataFrame | None = None, include_embeddings: Union[bool, Literal['separate']] = False, export_format: Literal['csv', 'parquet', 'excel'] = 'csv') -> DataFrameExport the dataset as a table (CSV, Parquet, or Excel).
Args:
output_path: Path to save the export. If None, only returns the DataFrame.dist_results: Optional DataFrame from compute_reference_distances.include_embeddings: If True, adds embedding columns; if ‘separate’, saves embeddings to a companion .npy file.export_format: Output format: ‘csv’, ‘parquet’, or ‘excel’.
Returns:
DataFrame with image_index, image_path, metadata, properties, distances, and embeddings (if requested).
Properties
Section titled “Properties”compute_properties
PhenoMeProperties.compute_properties( self, metadata_config: typing.Any | None = None, property_preset: str | None = None, additional_property_functions: dict[str, collections.abc.Callable | list[collections.abc.Callable]] | None = None, checkpoint_path: str | None = None, save_every: int = 50, n_jobs: int = 1, lazy_checkpoint: bool = True, force_update: bool = False, include_metadata: bool = False, progress_callback: collections.abc.Callable[[int, int, str], None] | None = None) -> DataFrameCompute per-image properties using presets and/or custom functions.
Each function receives a 2D image slice and/or mask slice and returns
a dict[str, float]. When multiple channels exist, properties are
suffixed with _ch{idx}, or with labels from find_files /
set_file_df channel_names when provided (e.g. _DAPI).
Use property_preset for built-in property sets and
additional_property_functions to add custom functions on top
(optional).
This method now relies solely on file_df (typically produced by
PhenoMe.find_files) for resolving image and mask
paths. Legacy image_dir / mask_dir parameters are no longer
supported.
Images used for properties are loaded, laid out as HxWxC, then scaled
with normalize_by_dtype_max (same 0-1 rule as TypeMaxNorm). Masks
are loaded and laid out the same way, then binarized
(foreground = pixels > 0.5; no intensity scaling). A concise
processing pipeline of those applied steps is logged at INFO.
Args:
metadata_config: Optional MetadataBase instance (e.g. fromphenome.metadata). When it hasmask_dirandmask_filename_column, uses explicit mask lookup.property_preset: Preset name. Use"none"orNonefor no preset. Valid:"none","basic","shape","intensity","standard","complete".additional_property_functions: Extra property functions to add on top of preset. Dict of {requirement: fn_or_list} where requirement is"image","mask","both", or"any". Merged with preset when both are used; used alone whenproperty_presetis"none"orNone.checkpoint_path: HDF5 checkpoint for incremental persistence.save_every: Commit frequency (images) when checkpointing.n_jobs: Number of parallel jobs for property computation.1(default) = sequential. Use-1to use all available CPU cores.lazy_checkpoint: If True (default), keep the checkpoint file open. If False, load all data into RAM and close the file.force_update: If True andcheckpoint_pathpoints to an existing file, clears stored properties in that file, writes current_processing_paramsinto the checkpoint config, and recomputes properties for every image (no resume). Use this after changingproperty_presetor custom functions, or after log warnings about missing checkpoint property keys. Ifcheckpoint_pathis set but the file does not exist yet, only an info log is emitted; computation proceeds as for a new checkpoint.include_metadata: If True, add metadata columns to the returned DataFrame.progress_callback: Optional(current, total, desc) -> Nonehook for GUI/notebook progress bars. Invoked after each image (sequential) or after each parallel batch. When set, the defaulttqdmbar is disabled. Default: None.
Returns:
DataFrame with image_index, image_path, image_name, property values,
and optional metadata columns.
Also populates results.properties as List[dict] (one dict per image).
Raises:
ValueError: If no images processed or invalid preset/requirement keys are used.TypeError: Ifadditional_property_functionsvalues are not callable.
Example:
Drive a napari/Qt progress bar from a worker thread:
>>> def on_progress(current, total, desc):... # update Qt/napari progress on the main thread... progress.setValue(int(100 * current / total) if total else 0)... progress.setLabelText(desc)>>> pm.compute_properties(... property_preset="basic",... progress_callback=on_progress,... )property_stats_by_group
PhenoMeProperties.property_stats_by_group( self, group_by: list[str] | None = None, properties: list[str] | None = None, print_table: bool = True, print_properties: list[str] | None = None, group_column_width_max: int = 25, content_col_width_max: int = 25) -> DataFrameGroup properties, compute per-group statistics, and optionally print a table.
Uses _build_properties_dataframe from computed
results.properties and metadata (same source as
compute_properties).
Args:
group_by: Metadata columns to group by. If None, auto-selects first 2.properties: Property columns to include in aggregation. If None, auto-detects numeric. Matching is case-insensitive; names must be unique ignoring case.print_table: If True, log a formatted mean±std table.print_properties: Subset of properties to show in the table. If None, shows all. Matching is case-insensitive; names must be unique ignoring case.group_column_width_max: Maximum width for each grouping column when printing.content_col_width_max: Maximum width for each property statistic column when printing.
Returns:
Aggregated DataFrame with mean/std/min/max per property per group.
Raises:
TypeError: If group_by/properties are invalid types.ValueError: If group_by keys are not in DataFrame columns.
top_properties_different_from_reference
PhenoMeProperties.top_properties_different_from_reference( self, df: pandas.DataFrame, reference_group: dict[str, typing.Any], k: int = 5, properties: list[str] | None = None, metric: Literal['cohens_d', 'mean_diff'] = 'cohens_d', print_output: bool = True, group_column_width_max: int = 25, top_property_col_width_max: int = 25, top_effect_col_width_min: int = 12) -> DataFrameFor each non-reference group, return the top k properties that most differentiate it from the reference.
Uses Cohen’s d (effect size) by default to rank properties by how different each group is from the reference. Both higher and lower values count as “different” (uses |d|).
Cohen’s d formula (pooled): d = (mean_group - mean_ref) / s_pooled, where s_pooled = sqrt([(n_ref-1)*std_ref² + (n_other-1)*std_other²] / (n_ref + n_other - 2)). Requires sample std (ddof=1) from property_stats_by_group.
Args:
df: Aggregated DataFrame from property_stats_by_group().reference_group: Dict mapping grouping column names to values (e.g. {“drug”: “Control”, “time”: “60_min”}).k: Number of top properties per group.properties: Property names to consider. If None, uses all in DataFrame. Matching is case-insensitive; names must be unique ignoring case.metric: ‘cohens_d’ (effect size) or ‘mean_diff’ (absolute mean difference).print_output: If True, pretty-print the results.group_column_width_max: Maximum width for each grouping column in the printed table.top_property_col_width_max: Max width for the property name column in the printed table.top_effect_col_width_min: Min width for Cohen’s d and mean diff columns when printing.
Returns:
DataFrame with columns: grouping cols, property, effect_size (Cohen’s d when
metric='cohens_d'), mean_diff, ref_mean, group_mean, rank. One row per
(group, property) for top-k only.
Advanced analysis
Section titled “Advanced analysis”correct_batches
PhenoMeBatchCorrection.correct_batches( self, batch_metadata_key: 'str', method: 'MethodName' = 'sphering', source: 'SourceName' = 'embeddings', property_keys: 'list[str] | None' = None, control_filters: 'dict[str, Any] | None' = None, inplace: 'bool' = True, checkpoint_path: 'str | None' = None, chunk_size: 'int' = 4096, force: 'bool' = False, ridge_multiplier: 'float' = 0.001, min_controls: 'int' = 2) -> dict[str, Any]Correct plate-to-plate variation using control wells per batch.
Call after :meth:process_images (and optionally after compute_properties when
source='properties'). For lazy checkpoints, embeddings are read and rewritten
in chunks of chunk_size rows.
Args:
batch_metadata_key: Metadata column for batch / plate id (case-insensitive).method:"sphering"(default) or"zscore".source:"embeddings"or"properties".property_keys: Property names to correct whensource='properties'. If None, uses all keys from :meth:get_available_property_keys.control_filters: Same semantics asfilter_indicesfilters (required).inplace: If True, updatesresults.embeddingsorresults.propertiesin place.checkpoint_path: If set, persist batch-correction metadata and/or export results.- If
None(default): Corrections still apply (RAM or an already-open lazy checkpoint), but nothing new is written for persistence—no/batch_correctionstats on disk and no new HDF5 via :meth:save_results. - If
str: With an open checkpoint, it is cloned to this path first; stats are saved under/batch_correctionwhen a DB is active; in eager mode (no DB), results are saved to this path after correction.
- If
chunk_size: Rows per chunk for embedding I/O and property scatter.force: If True, allow re-applying correction after a previous run in this session.ridge_multiplier: Covariance ridge strength for sphering (see :func:compute_batch_stats).min_controls: Minimum control wells required per batch.
Returns:
Dict with batch stats summary (batches, controls_per_batch, method, …).
compute_clustering
PhenoMeAnalysis.compute_clustering( self, source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', n_clusters: int = 5, clustering_method: Literal['kmeans', 'dbscan', 'gmm'] = 'kmeans', property_keys: list[str] | None = None, filters: dict[str, typing.Any | list[typing.Any]] | None = None, exclude: dict[str, typing.Any | list[typing.Any]] | None = None, random_state: int | None = None, normalize: bool = True, reduce_dim: int | None = 100, reduce_method: Literal['pca', 'tsne', 'umap'] = 'pca', return_silhouette: bool = False, dbscan_eps: float | None = None, dbscan_min_samples: int | None = None, include_metadata: bool = False) -> pandas.DataFrame | tuple[pandas.DataFrame, float | None]Perform clustering and store labels in metadata[i]['cluster'] for each image.
Args:
source:'embeddings','properties', or'combined'.n_clusters: Number of clusters (k-means, GMM). Ignored forclustering_method='dbscan'.clustering_method:'kmeans'(default),'dbscan', or'gmm'.property_keys: Property subset when source=‘properties’.filters: Optional metadata filters.exclude: Optional metadata exclusions (same structure as filters).random_state: Seed for k-means/GMM. If None, uses pipeline seed when available.normalize: Whether to normalize data before clustering (default: True). For embeddings, uses L2 normalization. For properties, uses StandardScaler.reduce_dim: If set, apply dimensionality reduction before clustering (default: 100). Only applied when data has more features than reduce_dim. Set to None to disable dimensionality reduction.reduce_method: Method for dimensionality reduction when reduce_dim is set:'pca'(fast, linear),'tsne'(non-linear, slower),'umap'(non-linear, preserves structure). Default:'pca'.return_silhouette: If True, compute and return the silhouette score (default: False). Returns(df, score); score is None if it could not be computed. Silhouette requires at least 2 clusters and 2 samples per cluster.dbscan_eps: Maximum distance between two samples for DBSCAN (only when clustering_method=‘dbscan’). If None, uses 0.5.dbscan_min_samples: Minimum samples in a neighborhood for DBSCAN (only when clustering_method=‘dbscan’). If None, uses max(2, n_samples // 20).include_metadata: If True, add metadata columns to the returned DataFrame.
Returns:
If return_silhouette=False: Tier-A DataFrame with image_index, image_path, image_name, cluster, and optional metadata. If return_silhouette=True: Tuple of (df, silhouette_score). Score is None if it could not be computed. Cluster labels are 0..K-1; None for excluded/noise.
detect_outliers
PhenoMeAnalysis.detect_outliers( self, method: Literal['z-score', 'iqr'] = 'z-score', threshold: float = 3.0, group_by: str | list[str] | None = None, filters: dict[str, typing.Any] | None = None, exclude: dict[str, typing.Any] | None = None, source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, normalize: bool = True, drop_outliers: bool = False, plot: bool = True) -> DataFrameDetect outliers based on distance to centroid.
When source is 'embeddings' and normalize is True (default), embeddings
are L2-normalized and group centroids use the arithmetic mean (not necessarily
a unit vector). Euclidean distance to that centroid is not the same as angular
distance to the mean direction; consider property-based or combined sources when
a different geometry is needed.
Args:
method:'z-score'or'iqr'.threshold: Threshold multiplier.group_by: Property/metadata key(s) for grouping. Can be a single string or a list of strings. If a list is provided, groups are formed by combining values from all specified columns (e.g., “drug1-10uM”). If None, all images are treated as one group.filters: Optional metadata filters.exclude: Optional metadata exclusions (same structure as filters).source: Feature space for outlier detection:'embeddings': model embeddings'properties': scalar phenotypic properties'combined': normalized concatenation of embeddings and properties.
property_keys: Property subset when source is'properties'or'combined'.normalize: Whether to normalize data before outlier detection (default: True). For embeddings, uses L2 normalization. For properties, uses StandardScaler.drop_outliers: If True, remove detected outliers from self.results (default: False).plot: If True (default), shows one matplotlib figure per group: subplots for detected outliers and a figure title naming the group (Group: …). Images are shown without pipelineimage_transforms(same asapply_transforms=Falsefor display).
Returns:
pd.DataFrame: Tier-A DataFrame containing detected outliers. Columns include:
image_index: Global image index.image_path: Path to the image file.image_name: Basename of the image file.- Additional columns for each grouping key (if group_by was set).
threshold: The threshold value used for the image’s group.distance_to_centroid: Distance to the group centroid. If no outliers are detected, returns an empty DataFrame. The index is a range index from 0 to N-1.
find_prototypes
PhenoMeAnalysis.find_prototypes( self, cluster_col: str | list[str] | None = 'cluster', n_prototypes: int = 5, source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, filters: dict[str, typing.Any] | None = None, exclude: dict[str, typing.Any] | None = None, metric: str = 'euclidean', normalize: bool = True, plot: bool = True) -> DataFrameFind images closest to each group centroid.
Args:
cluster_col: Property/metadata key(s) for grouping. Can be a single string or a list of strings. If a list is provided, groups are formed by combining values from all specified columns (e.g., “drug1-10uM”). If None, all images are treated as one group.n_prototypes: How many prototypes per group.source:'embeddings','properties', or'combined'.property_keys: Property subset when source=‘properties’.filters: Optional metadata filters.exclude: Optional metadata exclusions (same structure as filters).metric:'euclidean'or'cosine'. Withmetric='euclidean', the group centroid is the arithmetic mean (even when embeddings are L2-normalized). Withmetric='cosine', the centroid direction is L2-normalized before ranking by similarity.normalize: Whether to normalize data before finding prototypes (default: True). For embeddings, uses L2 normalization. For properties, uses StandardScaler.plot: If True (default), shows one matplotlib figure per group: subplots for that group’s prototypes and a figure title naming the group (Group: …). Images are shown without pipelineimage_transforms(same asapply_transforms=Falsefor display).
Returns:
pd.DataFrame: DataFrame with one row per prototype. Columns include grouping
keys (split into individual columns if multiple), image_index,
image_path, image_name, and rank_in_group.
analyze_group_enrichment
PhenoMeAnalysis.analyze_group_enrichment( self, group_by: str | list[str] = 'cluster', property_keys: list[str] | None = None, filters: dict[str, typing.Any] | None = None, exclude: dict[str, typing.Any] | None = None, plot: bool = True, return_fig: bool = False, top_k: int | None = None, figsize: tuple[int, int] = (10, 6), title: str | None = None, correct_multiple_testing: bool = True) -> pandas.DataFrame | tuple[pandas.DataFrame, Any]Compute z-score enrichment of properties per group (e.g. cluster labels).
For each group, z-scores are computed against a leave-group-out
population (all samples except the current group). When
correct_multiple_testing is True (default), Welch two-sample t-tests
(group vs leave-one-out rest) are run per property and Benjamini-Hochberg
FDR correction is applied across all (group, property) pairs; a
significant column is added to the output DataFrame.
Args:
group_by: Property/metadata key(s) holding group labels (e.g."cluster"). Can be a single string or a list of strings for composite grouping.property_keys: Properties to analyse (all numeric if None).filters: Optional metadata filters.exclude: Optional metadata exclusions (same structure as filters).plot: If True (default), show an interactive Plotly faceted bar chart (one row per group). If False, log a plain-text summary via the package logger instead (unless return_fig requests a figure).return_fig: If True, return a tuple(df, fig)wherefigis the Plotly figure. When return_fig is True,fig.show()is not called; useplot=Truewithreturn_fig=Falsefor the default interactive display.top_k: Max properties per group in the figure and in the text summary (None= all).figsize: Figure size(width, height)in inches for the Plotly layout.title: Optional figure title.correct_multiple_testing(bool): If True (default), run Welch t-tests per (group, property), apply Benjamini-Hochberg FDR, and addp_value/significantcolumns (alpha = 0.05).
Returns:
pd.DataFrame or tuple[pd.DataFrame, Any]:
- If return_fig is False (default): pd.DataFrame with columns: [group_by columns], property, score, mean_group, mean_pop, p_value, abs_score, and optionally significant when correct_multiple_testing is True.
- If return_fig is True: A tuple (enrichment_df, fig).
compute_component_correlation
PhenoMeAnalysis.compute_component_correlation( self, method: Literal['pca', 'tsne', 'umap'] = 'pca', n_components: int = 2, source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, filters: dict[str, typing.Any] | None = None, exclude: dict[str, typing.Any] | None = None, top_k: int | None = None, normalize: bool = True, correlation_method: Literal['pearson', 'spearman', 'distance_correlation', 'mutual_info'] = 'pearson', plot: bool = True, return_fig: bool = False, figsize: tuple[int, int] = (10, 6)) -> pandas.DataFrame | tuple[pandas.DataFrame, Any]Correlate dim-reduction components with phenotypic properties.
Args:
method: Dimensionality reduction method.n_components: Number of components to compute.source:'embeddings','properties', or'combined'.property_keys: Property subset when source is'properties'or'combined'.filters: Optional metadata filters.exclude: Optional metadata exclusions (same structure as filters).top_k: Number of top correlations to return in summary and per facet in the plot.normalize: Whether to normalize data before computing correlations (default: True).- Source data (embeddings/properties): Normalized before dimensionality reduction (L2 normalization for embeddings, StandardScaler for properties)
- Properties: Normalized using StandardScaler before correlation computation
correlation_method: Correlation method to use. Options:'pearson': Pearson correlation coefficient (default, linear relationships)'spearman': Spearman rank correlation (monotonic relationships, robust to outliers)'distance_correlation': Distance correlation (detects non-linear relationships, requiresdcorlibrary:pip install dcor)'mutual_info': MI-derived correlation coefficient (detects any dependency, mapped to [0, 1] via the Gaussian bivariate transform, not standard NMI)
plot: If True (default), show an interactive Plotly faceted bar chart (one row per component). If False, log a plain-text summary via the package logger instead (unless return_fig requests a figure).return_fig: If True, return a tuple of(correlation_df, figure). Whenreturn_figis True,fig.show()is not called.figsize: Figure size(width, height)in inches for the Plotly layout.
Returns:
pd.DataFrame | tuple[pd.DataFrame, Any]:
- If
return_figis False (default): Correlation DataFrame with apropertycolumn andcomponent_1,component_2, … columns. - If
return_figis True: Returns a tuple of(correlation_df, figure).
Raises:
ValueError: If dimensionality reduction fails, no components are found, no numeric properties are available, or correlation computation fails.
Examples:
Get correlations directly::
corr = pheno.compute_component_correlation(plot=False)
Get correlations and save the figure::
corr, fig = pheno.compute_component_correlation(return_fig=True) fig.write_html("component_corr.html")compute_embedding_property_correlations
PhenoMeAnalysis.compute_embedding_property_correlations( self, property_keys: list[str] | None = None, normalize: bool = True, method: Literal['pearson', 'spearman', 'distance_correlation', 'mutual_info'] = 'pearson', n_jobs: int = 1) -> dictCompute correlations between embedding dimensions and phenotypic scalar properties.
This is the time-consuming step that computes raw correlations for each property.
Args:
property_keys: Property names (all if None).normalize: Whether to normalize data before computing correlations (default: True).- Embeddings: L2 normalization (unit norm)
- Properties: StandardScaler normalization (zero mean, unit variance)
method: Correlation method to use. Options:'pearson': Pearson correlation coefficient (default, linear relationships)'spearman': Spearman rank correlation (monotonic relationships, robust to outliers)'distance_correlation': Distance correlation (detects non-linear relationships, requiresdcorlibrary:pip install dcor)'mutual_info': MI-derived correlation coefficient (detects any dependency, mapped to [0, 1] via the Gaussian bivariate transform, not standard NMI)
Returns:
Dict mapping property names to correlation arrays (n_dims,)
summarize_embedding_property_correlations
PhenoMeAnalysis.summarize_embedding_property_correlations( self, correlations: dict[str, numpy.ndarray], order_by: str = 'mean_abs', top_k: int | None = 20, plot: bool = True, return_fig: bool = False, figsize: tuple[int, int] = (10, 8), title: str = 'Property Correlations with Embeddings', embedding_shape: tuple[int, int] | None = None, n_properties: int | None = None, correlation_method: str = 'pearson') -> pandas.DataFrame | tuple[pandas.DataFrame, Any | None]Summarize embedding-property correlations, optionally plot, and/or return a Plotly figure.
Computes per-property mean_abs, std (across dimensions), max_abs, min_abs,
and mean, orders rows by order_by, and either shows a
horizontal violin plot of the distribution of |r| across dimensions per property or logs
a plain-text table when plot is False.
Args:
correlations: Output from :meth:compute_embedding_property_correlations(dict mapping property names to correlation arrays).order_by: Metric used to sort properties (descending):mean_abs|max_abs|mean|std.top_k: Number of top properties shown in the plot and listed intop_properties; full sorted table is always insummary(None = all).plot: If True (default), show a Plotly violin plot of |r| per dimension for the toptop_kproperties byorder_by(None = all). Highest metric at the top of the y-axis.return_fig: If True, return both the summary DataFrame and the Plotly figure.figsize: Figure size in inches, converted to pixels for Plotly layout.title: Chart title.embedding_shape: Shape of embeddings (n_samples, n_dims) for metadata display.n_properties: Number of properties processed for metadata display.correlation_method: Method used for correlation computation for metadata display.
Returns:
Sorted DataFrame with property, mean_abs, std, max_abs, min_abs,
and mean. If return_fig is True, returns (summary, figure).
compute_multivariate_interpretability
PhenoMeAnalysis.compute_multivariate_interpretability( self, method: Literal['pca', 'tsne', 'umap'] = 'tsne', component: int = 1, model_type: Literal['lasso', 'random_forest'] = 'lasso', property_keys: list[str] | None = None, filters: dict[str, typing.Any] | None = None, exclude: dict[str, typing.Any] | None = None, normalize: bool = True, cv: int = 5, stratify_by: str | None = None, rf_n_estimators: int = 100, seed: int | None = None, plot: bool = True, return_fig: bool = False, return_meta: bool = False, top_k: int = 10, figsize: tuple[int, int] = (10, 8)) -> pandas.DataFrame | tuple[pandas.DataFrame, dict[str, Any]] | tuple[pandas.DataFrame, Any] | tuple[pandas.DataFrame, dict[str, Any], Any]Explain a dimensionality reduction component using LASSO or Random Forest.
Calculates which phenotypic properties (features) best explain the variability seen in a deep learning embedding dimension (the target, usually t-SNE 1 or 2).
Workflow: dimensionality reduction is run on the full filtered embedding set
first; regression then relates classical properties to the resulting axis
coordinates. For transductive methods (t-SNE, UMAP), axis coordinates depend on
all samples, so the returned r2 (LASSO) describes descriptive on-axis alignment
on the same dataset—not independent generalization to new plates or refit
embeddings. Prefer drivers for hypothesis generation; interpret r2 as an
effect-size-style summary unless DR and evaluation are nested with proper holdouts.
Args:
method: Dimensionality reduction method (‘pca’, ‘tsne’, or ‘umap’).component: Which component to explain (1, 2, …).model_type: The regression model to use (‘lasso’ or ‘random_forest’). ‘lasso’ uses L1 regularization for linear, sparse explanations. ‘random_forest’ captures non-linear relationships.property_keys: Subset of properties to use as features.filters: Optional metadata filters.exclude: Optional metadata exclusions.normalize: Whether to normalize embeddings before dimensionality reduction (default: True). Property features are scaled fold-wise inside the LASSO pipeline (not globally) to avoid cross-validation leakage.cv: Number of cross-validation folds (only for ‘lasso’).stratify_by: Optional metadata key (e.g."Treatment","Batch") to stratify LASSO folds by class balance. Requires at leastcvsamples per class; otherwise falls back to unstratified KFold. Balances folds forlambdaselection but does not correct global t-SNE/UMAP target leakage.rf_n_estimators: Number of trees (only for ‘random_forest’).seed: Random seed for reproducibility. If None, uses the pipeline’sseedwhen set.plot: If True (default), show an interactive Plotly bar chart of top drivers. If False, log a plain-text summary via the package logger instead.return_fig: If True, include the Plotly figure in the return value (see Returns). Whenreturn_figis True,fig.show()is not called; useplot=Truewithreturn_fig=Falsefor interactive display.return_meta: If True, include a run-parameters dict in the return value.top_k: Number of top drivers to show in the plot or text summary.figsize: Figure size(width, height)passed through to Plotly layout; each value is multiplied by 100 to set width and height in layout pixels.
Returns:
pd.DataFrame or tuple, depending on return_meta and return_fig:
- Default:
drivers_dfwith columnsfeature,weight. return_meta=True:(drivers_df, meta); meta includes r2 (descriptive on-axis fit for globally fit DR targets), n_samples, n_features, method, model_type, target_component.return_fig=True:(drivers_df, fig)or(drivers_df, meta, fig)when return_meta is also True.
Reporting
Section titled “Reporting”generate_report
PhenoMe.generate_report( self, output_path: str = 'pheno_report.html', title: str = 'PhenoMe Analysis Report', config: typing.Any | None = None, **overrides: Any) -> strGenerate a comprehensive standalone HTML report from phenotyping results.
Uses ReportConfig as the primary source of options. Pass config= for full control, or use **overrides to tweak individual settings (e.g. include_plots=False).
Args:
output_path: Path to save the HTML file.title: Report title displayed at the top.config: Optional ReportConfig for defaults. If None, uses ReportConfig(). **overrides: Any ReportConfig field to override (e.g. include_plots=False, outlier_threshold=2.5, n_clusters=10).
Returns:
Path to the generated HTML file.
get_run_settings
PhenoMe.get_run_settings( self) -> dictReturn the silent run log as a JSON-serializable dict.
Includes init parameters, environment, recorded method steps, processing_params, and the processing pipeline when available. Does not print or write files.
image_preview_png_bytes
_ImageDisplayMixin.image_preview_png_bytes( self, idx: int, distance_results: pandas.DataFrame | None = None, channels: int | list | None = None, title_fields: list[str] | None = None, show_extra_info: bool = False, apply_transforms: bool = True, downsample: int | None = 720) -> tupleRasterize the same view as plot_image_by_index to PNG bytes.
Used by the interactive explorer with ipywidgets.Image because matplotlib
display() / plt.show() from Plotly click callbacks does not reliably
target a nested Output widget (output goes to the cell or nowhere).
When show_extra_info is True, also emits the same details as
plot_image_by_index via logging and returns their text for UI display.
Returns:
(png_bytes, details_text_or_none) — details_text_or_none is the
formatted extra-info block when show_extra_info is True and details exist.
load_embeddings
PhenoMe.load_embeddings( self, path: str | None = None, lazy_checkpoint: bool = True) -> NoneLoad an embeddings checkpoint into the pipeline.
Same path/file_df rebase flow as load_results,
but requires the file to contain embeddings. Sets self._db for lazy
get_embeddings() access when lazy_checkpoint=True.
Typical two-file workflow::
pheno.find_files(data_dir)pheno.load_embeddings("run_embeddings.h5")pheno.load_properties("run_properties.h5")Args:
path: Path to .h5 or .hdf5 embeddings checkpoint.lazy_checkpoint: If True (default), keep the checkpoint file open. If False, load all data into RAM and close the file.
Raises:
FileNotFoundError: If file does not exist.ValueError: If file format is not recognised as HDF5, or the checkpoint has no embeddings.RuntimeError: If file_df is not available (call find_files or set_file_df first).ConcurrentCheckpointAccessError: If the checkpoint is already open in another notebook or process (only one instance can access it at a time).
load_properties
PhenoMe.load_properties( self, path: str | None = None, lazy_checkpoint: bool = True) -> NoneLoad a properties checkpoint into the pipeline.
When results already contain images (e.g. after
load_embeddings or
process_images), properties are aligned by metadata key then path
and overlaid onto the existing rows without replacing an embeddings
self._db from a different file.
When no results are loaded yet, performs a full checkpoint setup like
load_results. For a
properties-only file, the HDF5 handle is closed after load (properties
are eager); self._db is kept only if that file also has embeddings.
Args:
path: Path to .h5 or .hdf5 properties checkpoint.lazy_checkpoint: Used on cold start only. If True (default), keep the file open when it also stores embeddings. Ignored when overlaying onto existing results.
Raises:
FileNotFoundError: If file does not exist.ValueError: If file format is not recognised as HDF5, or the checkpoint has no property content, or overlay matches no rows.RuntimeError: On cold start, if file_df is not available (call find_files or set_file_df first).ConcurrentCheckpointAccessError: If the checkpoint is already open in another notebook or process (only one instance can access it at a time).
plot_centroids
_DRPlotsMixin.plot_centroids( self, group_by: str | list[str], method: Literal['pca', 'tsne', 'umap'] = 'pca', n_components: int = 2, filters: dict[str, typing.Any | list[typing.Any]] | None = None, exclude: dict[str, typing.Any | list[typing.Any]] | None = None, source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, show_points: bool = True, show_centroids: bool = True, figsize: tuple[int, int] = (10, 8), return_fig: bool = False, normalize: bool = True, **dr_kwargs: Any) -> AnyPlot centroids of groups in reduced embedding space.
plot_image_by_index
_ImageDisplayMixin.plot_image_by_index( self, idx: int, distance_results: pandas.DataFrame | None = None, channels: int | list | None = None, figsize: tuple = (6, 6), title_fields: list[str] | None = None, show_extra_info: bool = True, apply_transforms: bool = False, downsample: int | None = 720, ax: Any = None, return_fig: bool = False, show_mask_overlay: bool = False) -> AnyPlot a specific image by its index.
Args:
idx: Image index in resultsdistance_results: Optional distance computation results DataFramechannels: specific channels to plotfigsize: Figure sizetitle_fields: Optional list of field names to display in titleshow_extra_info: If True, prints detailed information after plottingapply_transforms: If True, apply pipeline image_transformsdownsample: If not None, approximate desired size (in pixels) for the longest image edge when downsampling. The final image size may differ slightly due to integer stepping. If None, no downsampling.ax: Optional matplotlib axes to plot on. If provided, a new figure is not created.return_fig: If True, return the displayed image as a NumPy array and do not open a matplotlib figure. Mask overlay, when requested, is composited onto that array.show_mask_overlay: If True, draws the segmentation mask as a semi-transparent yellow overlay with a crisp contour. Requires masks to have been discovered viamask_dirinfind_files. Useful for verifying that masks load correctly and spatially align with their images.
Returns:
The image as a NumPy array when return_fig is True; otherwise None.
plot_pca
_DRPlotsMixin.plot_pca( self, n_components: int = 2, color_by: str | None = None, figsize: tuple[int, int] = (10, 8), source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, filters: dict[str, typing.Any | list[typing.Any]] | None = None, exclude: dict[str, typing.Any | list[typing.Any]] | None = None, hover_features: list[str] | None = None, return_fig: bool = False, render_mode: Literal['auto', 'svg', 'webgl'] = 'webgl', normalize: bool = True, sample_size: int | None = None, **dr_kwargs: Any) -> AnyPlot PCA of embeddings, properties, or combined features (Plotly, WebGL by default).
plot_tsne
_DRPlotsMixin.plot_tsne( self, n_components: int = 2, perplexity: float = 30.0, color_by: str | None = None, figsize: tuple[int, int] = (10, 8), source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, filters: dict[str, typing.Any | list[typing.Any]] | None = None, exclude: dict[str, typing.Any | list[typing.Any]] | None = None, hover_features: list[str] | None = None, return_fig: bool = False, render_mode: Literal['auto', 'svg', 'webgl'] = 'webgl', normalize: bool = True, sample_size: int | None = None, **dr_kwargs: Any) -> AnyPlot t-SNE of embeddings, properties, or combined features (Plotly, WebGL by default).
plot_umap
_DRPlotsMixin.plot_umap( self, n_components: int = 2, n_neighbors: int = 15, min_dist: float = 0.1, color_by: str | None = None, figsize: tuple[int, int] = (10, 8), source: Literal['embeddings', 'properties', 'combined'] = 'embeddings', property_keys: list[str] | None = None, filters: dict[str, typing.Any | list[typing.Any]] | None = None, exclude: dict[str, typing.Any | list[typing.Any]] | None = None, hover_features: list[str] | None = None, return_fig: bool = False, render_mode: Literal['auto', 'svg', 'webgl'] = 'webgl', normalize: bool = True, sample_size: int | None = None, **dr_kwargs: Any) -> AnyPlot UMAP of embeddings, properties, or combined features (Plotly, WebGL by default).
print_distance_summary
_DistancePlotsMixin.print_distance_summary( self, distance_results: pandas.DataFrame, group_by: str | None = None, dist_range: tuple = (0, 100)) -> NonePrint distance summary statistics without plotting. Safe to use when enable_plots=False.
Args:
distance_results: DataFrame containing ‘distance’ columngroup_by: Metadata key to group bydist_range: Tuple of (min, max) distances to include
Class PhenoMeResults
Section titled “Class PhenoMeResults”Typed container for per-image phenotyping data (PhenoMe.results).
Access fields via attributes: results.img_path, results.metadata,
results.properties, and results.embeddings.
Embeddings lifecycle
embeddings tracks the eager copy held by the pipeline:
- Not yet computed —
embeddings is None,pipeline._db is None - Lazy-backed (HDF5) —
embeddings is None,pipeline._db is not None(rows are fetched from disk on demand) - Eagerly computed —
embeddingsis annp.ndarrayof shape(N, D)
The former []-sentinel that mixed “uninitialised” with “lazy” with
“being filled during extraction” has been removed. In-progress embeddings
during extraction live in pipeline._emb_buffer (a plain List[np.ndarray])
and never touch this container until they are finalised.
Parameters:
img_path(list of str or list of list[str]): Per-image file path(s). Single-channel images use a plainstr; multi-channel images use aList[str]with one path per channel.metadata(list of dict): Per-image metadata dicts (e.g.{"drug": "DMSO", "time": "24h"}).properties(list of dict): Per-image computed scalar properties (e.g.{"intensity_mean": 0.42, "area": 1024}).embeddings(np.ndarray or None): Shape(N, D)float32 array, orNonewhen not available / when embeddings are stored lazily in an HDF5 file.
Accessors and inspection
Section titled “Accessors and inspection”has_embeddings
Property on PhenoMeResults
True if embeddings are stored eagerly (not lazy or absent).
embedding_dim
Property on PhenoMeResults
Embedding dimensionality, or 0 if no embeddings are stored.
clear
PhenoMeResults.clear( self) -> NoneReset all fields to empty state.
has_properties
Property on PhenoMeResults
True if at least one image has computed properties.
image_name
PhenoMeResults.image_name( self, idx: 'int') -> strReturn the basename of the primary path for image idx.
metadata_keys
Property on PhenoMeResults
Sorted unique metadata keys across all images (case-preserving).
metadata_value
PhenoMeResults.metadata_value( self, idx: 'int', key: 'str') -> AnyCase-insensitive metadata lookup for image idx.
n_images
Property on PhenoMeResults
Number of images stored.
primary_path
PhenoMeResults.primary_path( self, idx: 'int') -> strReturn the primary (or only) file path for image idx.
property_keys
Property on PhenoMeResults
Sorted list of property names from the first non-empty properties dict.
property_value
PhenoMeResults.property_value( self, idx: 'int', key: 'str') -> float | NoneReturn a scalar property for image idx, or None if absent.
rebase_paths
PhenoMeResults.rebase_paths( self, data_dir: 'str', old_data_dir: 'str | None' = None) -> NoneUpdate all image paths by replacing old_data_dir with data_dir.
This is useful when moving a checkpoint and its dataset to a different machine or directory.
Parameters:
data_dir(str): The new base directory where the images are located.old_data_dir(str, optional): The old base directory to be replaced. If not provided, it is automatically detected by finding the common prefix of all stored paths. Special value “relative” indicates that stored paths are already relative and just need to be joined with data_dir.
