phenome 1.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phenome/__init__.py +180 -0
- phenome/_logging.py +99 -0
- phenome/core/__init__.py +107 -0
- phenome/core/_processing_pipeline_persist.py +112 -0
- phenome/core/batch_correction.py +384 -0
- phenome/core/dataframe_contract.py +175 -0
- phenome/core/math/__init__.py +38 -0
- phenome/core/math/combined_features.py +99 -0
- phenome/core/math/correlation.py +496 -0
- phenome/core/math/dimensionality_reduction.py +489 -0
- phenome/core/math/interpretability.py +361 -0
- phenome/core/pipeline_results.py +238 -0
- phenome/core/processing_pipeline.py +1679 -0
- phenome/core/property_utils.py +143 -0
- phenome/core/protocols.py +79 -0
- phenome/core/results_export.py +142 -0
- phenome/core/results_metadata.py +451 -0
- phenome/core/results_validation.py +49 -0
- phenome/core/run_log.py +227 -0
- phenome/io/__init__.py +50 -0
- phenome/io/_checkpoint_ops.py +523 -0
- phenome/io/_discovery_ops.py +106 -0
- phenome/io/_path_utils.py +42 -0
- phenome/io/batch_correction_ops.py +100 -0
- phenome/io/checkpoint.py +1878 -0
- phenome/io/checkpoint_alignment.py +502 -0
- phenome/io/discovery.py +629 -0
- phenome/mcp/__init__.py +5 -0
- phenome/mcp/docs_search.py +104 -0
- phenome/mcp/recipes.py +92 -0
- phenome/mcp/server.py +137 -0
- phenome/metadata/__init__.py +50 -0
- phenome/metadata/base.py +255 -0
- phenome/metadata/sources.py +247 -0
- phenome/mixins/__init__.py +79 -0
- phenome/mixins/_helpers.py +136 -0
- phenome/mixins/analysis.py +1885 -0
- phenome/mixins/batch_correction.py +357 -0
- phenome/mixins/dataset.py +219 -0
- phenome/mixins/distances.py +400 -0
- phenome/mixins/embedding_extractor.py +237 -0
- phenome/mixins/interactive/__init__.py +18 -0
- phenome/mixins/interactive/_html_components.py +333 -0
- phenome/mixins/interactive/_protocol.py +54 -0
- phenome/mixins/interactive/_utils.py +247 -0
- phenome/mixins/interactive/explorer.py +3500 -0
- phenome/mixins/properties/__init__.py +10 -0
- phenome/mixins/properties/_compute.py +1194 -0
- phenome/mixins/properties/_grouping_table.py +173 -0
- phenome/mixins/properties/_paths.py +270 -0
- phenome/mixins/properties/_report.py +157 -0
- phenome/mixins/properties/_workers.py +303 -0
- phenome/mixins/properties/grouping.py +516 -0
- phenome/mixins/properties/main.py +568 -0
- phenome/mixins/properties/matrix.py +383 -0
- phenome/mixins/visualization/__init__.py +10 -0
- phenome/mixins/visualization/_component_correlation_plots.py +254 -0
- phenome/mixins/visualization/_core.py +167 -0
- phenome/mixins/visualization/_distance_plots.py +278 -0
- phenome/mixins/visualization/_dr_plots.py +609 -0
- phenome/mixins/visualization/_group_enrichment_plots.py +271 -0
- phenome/mixins/visualization/_helpers.py +224 -0
- phenome/mixins/visualization/_image_display.py +568 -0
- phenome/mixins/visualization/_interpretability_plots.py +187 -0
- phenome/pipeline.py +2832 -0
- phenome/plotly_display.py +98 -0
- phenome/plugins/__init__.py +78 -0
- phenome/plugins/builtin_properties.py +174 -0
- phenome/plugins/registry.py +104 -0
- phenome/py.typed +0 -0
- phenome/report/__init__.py +63 -0
- phenome/report/_components.py +317 -0
- phenome/report/_section_helpers.py +127 -0
- phenome/report/context.py +53 -0
- phenome/report/generator.py +544 -0
- phenome/report/helpers.py +223 -0
- phenome/report/scripts.py +332 -0
- phenome/report/sections/__init__.py +38 -0
- phenome/report/sections/clustering.py +449 -0
- phenome/report/sections/correlation.py +75 -0
- phenome/report/sections/distance.py +159 -0
- phenome/report/sections/export.py +96 -0
- phenome/report/sections/gallery.py +232 -0
- phenome/report/sections/interpretability.py +125 -0
- phenome/report/sections/outlier.py +164 -0
- phenome/report/sections/overview.py +119 -0
- phenome/report/sections/property_stats.py +160 -0
- phenome/report/sections/run_settings.py +80 -0
- phenome/report/sections/visualization.py +121 -0
- phenome/report/styles.py +928 -0
- phenome/utils/__init__.py +119 -0
- phenome/utils/device.py +63 -0
- phenome/utils/display_names.py +65 -0
- phenome/utils/metadata.py +206 -0
- phenome/utils/model_wrapper.py +211 -0
- phenome/utils/path_utils.py +170 -0
- phenome/utils/progress.py +35 -0
- phenome/utils/property_factories.py +671 -0
- phenome/utils/transforms.py +319 -0
- phenome-1.4.1.dist-info/METADATA +165 -0
- phenome-1.4.1.dist-info/RECORD +105 -0
- phenome-1.4.1.dist-info/WHEEL +5 -0
- phenome-1.4.1.dist-info/entry_points.txt +2 -0
- phenome-1.4.1.dist-info/licenses/LICENSE +21 -0
- phenome-1.4.1.dist-info/top_level.txt +1 -0
phenome/__init__.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@section Core Pipeline
|
|
3
|
+
|
|
4
|
+
PhenoMe Package
|
|
5
|
+
|
|
6
|
+
A modular, **dataset-agnostic** and **model-agnostic** pipeline for phenotyping
|
|
7
|
+
analysis using deep learning embeddings.
|
|
8
|
+
|
|
9
|
+
**Overview:**
|
|
10
|
+
PhenoMe orchestrates the full phenotyping workflow: discover images, extract embeddings
|
|
11
|
+
with custom or pretrained models, compute cell/object properties, perform statistical
|
|
12
|
+
analysis, and generate interactive visualizations and reports.
|
|
13
|
+
|
|
14
|
+
**Public API:**
|
|
15
|
+
|
|
16
|
+
Core orchestration:
|
|
17
|
+
- `PhenoMe`: Main pipeline class; find images, process embeddings, compute properties, analyze.
|
|
18
|
+
- `PhenoMeResults`: Container for embeddings, properties, metadata, and analysis results.
|
|
19
|
+
|
|
20
|
+
Metadata helpers:
|
|
21
|
+
- `DefaultMetadata`: Simple key-value metadata.
|
|
22
|
+
- `PathTemplateMetadata`: Extract metadata from file paths using templates.
|
|
23
|
+
- `DataFrameMetadata`: Use a DataFrame as metadata source.
|
|
24
|
+
- `default_metadata_from_path`, `get_metadata_from_path`, `make_dataframe_metadata_fn`:
|
|
25
|
+
Metadata factory functions.
|
|
26
|
+
|
|
27
|
+
Model loading:
|
|
28
|
+
- `load_dinov2_model`: Load pretrained DINOv2 model.
|
|
29
|
+
|
|
30
|
+
Property factories (create custom cell/object properties):
|
|
31
|
+
- `create_regionprops_function`, `create_intensity_function`,
|
|
32
|
+
`create_masked_intensity_function`, `create_texture_function`,
|
|
33
|
+
`create_concentric_ring_function`, `create_blur_effect_function`,
|
|
34
|
+
`create_entropy_function`: Built-in property creators.
|
|
35
|
+
- `get_preset_property_functions`: List all available preset properties.
|
|
36
|
+
|
|
37
|
+
**Data flow:**
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
find_files()
|
|
41
|
+
↓ returns DataFrame of image paths
|
|
42
|
+
set_file_df() or directly via DataFrame
|
|
43
|
+
↓ store file locations
|
|
44
|
+
process_images(model)
|
|
45
|
+
↓ extract embeddings, populate results.embeddings
|
|
46
|
+
compute_properties(property_functions)
|
|
47
|
+
↓ compute morphological/biological features
|
|
48
|
+
compute_clustering() / detect_outliers() / ...
|
|
49
|
+
↓ statistical analysis
|
|
50
|
+
plot_pca() / plot_tsne() / plot_umap() / ...
|
|
51
|
+
↓ visualization
|
|
52
|
+
generate_report()
|
|
53
|
+
↓ HTML report
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Model compatibility:**
|
|
57
|
+
- **DINOv2:** Use `load_dinov2_model()` or wrap with `DinoV2ModelWrapper`.
|
|
58
|
+
- **Custom:** Subclass `ModelWrapper`, implement `_get_embeddings(images) -> (B, D)`.
|
|
59
|
+
|
|
60
|
+
**For advanced usage, import from submodules:**
|
|
61
|
+
- `phenome.core`: Math utilities (DR, correlation, validation, export).
|
|
62
|
+
- `phenome.io`: File I/O (`read_image`, `FileDiscovery`, `CheckpointManager`).
|
|
63
|
+
- `phenome.mixins`: Composition classes (dataset, properties, analysis, visualization).
|
|
64
|
+
- `phenome.plugins`: Property registry and custom property registration.
|
|
65
|
+
- `phenome.report`: HTML report generation.
|
|
66
|
+
- `phenome.utils`: Device management, transforms, metadata and property factories.
|
|
67
|
+
|
|
68
|
+
**Logging:**
|
|
69
|
+
|
|
70
|
+
Progress messages are visible at INFO level by default:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import logging
|
|
74
|
+
logging.getLogger("phenome").setLevel(logging.WARNING) # Suppress
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Example:**
|
|
78
|
+
|
|
79
|
+
Basic phenotyping workflow:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from phenome import PhenoMe, load_dinov2_model
|
|
83
|
+
|
|
84
|
+
# Initialize pipeline
|
|
85
|
+
pm = PhenoMe(device="cuda", seed=42)
|
|
86
|
+
|
|
87
|
+
# Find images
|
|
88
|
+
pm.find_files("path/to/images/")
|
|
89
|
+
|
|
90
|
+
# Extract embeddings
|
|
91
|
+
model = load_dinov2_model(model_name="dinov2_vitb14")
|
|
92
|
+
pm.process_images(model, batch_size=32)
|
|
93
|
+
|
|
94
|
+
# Compute properties
|
|
95
|
+
pm.compute_properties(property_preset="basic")
|
|
96
|
+
|
|
97
|
+
# Analyze
|
|
98
|
+
pm.compute_clustering(method="kmeans", n_clusters=5)
|
|
99
|
+
pm.compute_pca()
|
|
100
|
+
|
|
101
|
+
# Visualize
|
|
102
|
+
pm.plot_pca(color_by="clustering")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**See Also:**
|
|
106
|
+
For architecture, design patterns, and extensibility, see the [Architecture guide](https://AAitorG.github.io/PhenoMe/guides/architecture/).
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
# Initialise notebook-safe logging on first import
|
|
110
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
111
|
+
|
|
112
|
+
from ._logging import get_logger as _get_logger
|
|
113
|
+
from .core import PhenoMeResults
|
|
114
|
+
from .metadata import (
|
|
115
|
+
DataFrameMetadata,
|
|
116
|
+
DefaultMetadata,
|
|
117
|
+
MetadataBase,
|
|
118
|
+
PathTemplateMetadata,
|
|
119
|
+
)
|
|
120
|
+
from .pipeline import PhenoMe
|
|
121
|
+
from .plotly_display import register_plotly_display_defaults
|
|
122
|
+
|
|
123
|
+
# Metadata functions and classes
|
|
124
|
+
from .utils.metadata import (
|
|
125
|
+
default_metadata_from_path,
|
|
126
|
+
get_metadata_from_path,
|
|
127
|
+
make_dataframe_metadata_fn,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Core workflow helpers
|
|
131
|
+
from .utils.model_wrapper import load_dinov2_model
|
|
132
|
+
|
|
133
|
+
# Property factories
|
|
134
|
+
from .utils.property_factories import (
|
|
135
|
+
create_blur_effect_function,
|
|
136
|
+
create_concentric_ring_function,
|
|
137
|
+
create_entropy_function,
|
|
138
|
+
create_intensity_function,
|
|
139
|
+
create_masked_intensity_function,
|
|
140
|
+
create_regionprops_function,
|
|
141
|
+
create_texture_function,
|
|
142
|
+
get_preset_property_functions,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
__version__ = version("phenome")
|
|
147
|
+
except PackageNotFoundError:
|
|
148
|
+
__version__ = "1.4.1"
|
|
149
|
+
|
|
150
|
+
__all__ = [
|
|
151
|
+
"DataFrameMetadata",
|
|
152
|
+
"DefaultMetadata",
|
|
153
|
+
"MetadataBase",
|
|
154
|
+
"PathTemplateMetadata",
|
|
155
|
+
"PhenoMe",
|
|
156
|
+
"PhenoMeResults",
|
|
157
|
+
"__version__",
|
|
158
|
+
# Property factories
|
|
159
|
+
"create_blur_effect_function",
|
|
160
|
+
"create_concentric_ring_function",
|
|
161
|
+
"create_entropy_function",
|
|
162
|
+
"create_intensity_function",
|
|
163
|
+
"create_masked_intensity_function",
|
|
164
|
+
"create_regionprops_function",
|
|
165
|
+
"create_texture_function",
|
|
166
|
+
# Metadata functions
|
|
167
|
+
"default_metadata_from_path",
|
|
168
|
+
"get_metadata_from_path",
|
|
169
|
+
# Property factories
|
|
170
|
+
"get_preset_property_functions",
|
|
171
|
+
# Default models
|
|
172
|
+
"load_dinov2_model",
|
|
173
|
+
"make_dataframe_metadata_fn",
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
_get_logger(__name__)
|
|
177
|
+
__author__ = "Aitor González-Marfil"
|
|
178
|
+
__author_github__ = "AAitorG"
|
|
179
|
+
|
|
180
|
+
register_plotly_display_defaults()
|
phenome/_logging.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Notebook-safe logging configuration for phenome.
|
|
3
|
+
|
|
4
|
+
Problem
|
|
5
|
+
-------
|
|
6
|
+
The standard ``logging.getLogger(__name__)`` pattern silently drops INFO
|
|
7
|
+
messages in Jupyter notebooks because Python's root logger defaults to
|
|
8
|
+
WARNING level and has no handler attached. If the user manually adds a
|
|
9
|
+
handler, duplicate output appears on every cell re-run.
|
|
10
|
+
|
|
11
|
+
Solution
|
|
12
|
+
--------
|
|
13
|
+
We configure a single package-level logger (``phenome``) with:
|
|
14
|
+
|
|
15
|
+
* A ``StreamHandler`` writing to ``sys.stderr`` (rendered by Jupyter as
|
|
16
|
+
plain text below the cell).
|
|
17
|
+
* INFO as default level, so progress messages are always visible.
|
|
18
|
+
* A guard that prevents adding duplicate handlers on module reload
|
|
19
|
+
(``%autoreload`` / repeated imports).
|
|
20
|
+
|
|
21
|
+
All sub-modules should call::
|
|
22
|
+
|
|
23
|
+
from ._logging import get_logger
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
instead of ``logging.getLogger(__name__)``.
|
|
27
|
+
|
|
28
|
+
Users who want to silence the output can do::
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
logging.getLogger("phenome").setLevel(logging.WARNING)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import logging
|
|
35
|
+
import sys
|
|
36
|
+
|
|
37
|
+
# Package-level logger name
|
|
38
|
+
_PKG_LOGGER_NAME = "phenome"
|
|
39
|
+
|
|
40
|
+
# Sentinel attribute on the package logger so we configure at most once.
|
|
41
|
+
_CONFIGURED_ATTR = "_phenome_configured"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_logger(name: str) -> logging.Logger:
|
|
45
|
+
"""Return a logger configured for notebook-safe output.
|
|
46
|
+
|
|
47
|
+
Typical usage at the top of each module::
|
|
48
|
+
|
|
49
|
+
from ._logging import get_logger
|
|
50
|
+
logger = get_logger(__name__)
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
name : str
|
|
55
|
+
Usually ``__name__`` of the calling module.
|
|
56
|
+
"""
|
|
57
|
+
_ensure_configured()
|
|
58
|
+
return logging.getLogger(name)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _ensure_configured() -> None:
|
|
62
|
+
"""Attach a StreamHandler to the package logger if not done already."""
|
|
63
|
+
pkg_logger = logging.getLogger(_PKG_LOGGER_NAME)
|
|
64
|
+
|
|
65
|
+
if getattr(pkg_logger, _CONFIGURED_ATTR, False):
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Library best-practice: add NullHandler so that if the user never
|
|
69
|
+
# configures logging at all, nothing breaks.
|
|
70
|
+
pkg_logger.addHandler(logging.NullHandler())
|
|
71
|
+
|
|
72
|
+
# Only add our StreamHandler if the package logger (or its parents)
|
|
73
|
+
# have no *effective* handlers yet. This avoids fighting with a
|
|
74
|
+
# user who already configured logging before importing us.
|
|
75
|
+
if not _has_effective_handler(pkg_logger):
|
|
76
|
+
handler = logging.StreamHandler(sys.stderr)
|
|
77
|
+
handler.setFormatter(logging.Formatter("%(message)s"))
|
|
78
|
+
pkg_logger.addHandler(handler)
|
|
79
|
+
|
|
80
|
+
# Set INFO as the default level for the package tree.
|
|
81
|
+
# Only override if the logger is still at NOTSET (i.e. the user hasn't
|
|
82
|
+
# explicitly set a level).
|
|
83
|
+
if pkg_logger.level == logging.NOTSET:
|
|
84
|
+
pkg_logger.setLevel(logging.INFO)
|
|
85
|
+
|
|
86
|
+
setattr(pkg_logger, _CONFIGURED_ATTR, True)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _has_effective_handler(logger: logging.Logger) -> bool:
|
|
90
|
+
"""Check if *logger* or any ancestor already has a non-NullHandler."""
|
|
91
|
+
current: logging.Logger | None = logger
|
|
92
|
+
while current:
|
|
93
|
+
for h in current.handlers:
|
|
94
|
+
if not isinstance(h, logging.NullHandler):
|
|
95
|
+
return True
|
|
96
|
+
if not current.propagate:
|
|
97
|
+
break
|
|
98
|
+
current = current.parent
|
|
99
|
+
return False
|
phenome/core/__init__.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@section Core Pipeline
|
|
3
|
+
|
|
4
|
+
Core utilities for math, validation, export, and results management.
|
|
5
|
+
|
|
6
|
+
This package provides the mathematical algorithms and data structures that underpin
|
|
7
|
+
the phenotyping pipeline. Key components:
|
|
8
|
+
|
|
9
|
+
**Results container:**
|
|
10
|
+
- `PhenoMeResults`: Typed container for embeddings, properties, metadata, and analysis results.
|
|
11
|
+
|
|
12
|
+
**Mathematical functions:**
|
|
13
|
+
- `run_dimensionality_reduction`: PCA, t-SNE, UMAP.
|
|
14
|
+
- `compute_pearson_correlation`, `compute_spearman_correlation`: Feature correlations.
|
|
15
|
+
- `compute_distance_correlation`: Distance correlation between features.
|
|
16
|
+
- `compute_mutual_info`: Mutual information between features.
|
|
17
|
+
- `compute_entropy`: Entropy of features.
|
|
18
|
+
- `build_combined_features`: Combine multiple features into single representation.
|
|
19
|
+
|
|
20
|
+
**Validation and export:**
|
|
21
|
+
- `validate_results`: Check results integrity (embeddings, properties, shapes).
|
|
22
|
+
- `build_export_dataframe`: Format results for export to CSV/Excel.
|
|
23
|
+
- `prepare_embedding_dataframe`: Prepare embeddings with metadata for analysis.
|
|
24
|
+
|
|
25
|
+
**Metadata handling:**
|
|
26
|
+
- `build_metadata_columns`: Extract and format metadata columns.
|
|
27
|
+
- `filter_indices`: Select rows by metadata criteria.
|
|
28
|
+
- `get_metadata_value`: Retrieve metadata value for a row.
|
|
29
|
+
- `get_all_metadata_keys`: List all available metadata keys.
|
|
30
|
+
- `metadata_to_stable_key`: Convert metadata to file-safe string.
|
|
31
|
+
|
|
32
|
+
**Protocols:**
|
|
33
|
+
- `PhenoMeProtocol`: Protocol (duck-type interface) for objects implementing the phenotyping API.
|
|
34
|
+
|
|
35
|
+
**See Also:**
|
|
36
|
+
For top-level analysis pipeline, see `phenome.PhenoMe`.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from .dataframe_contract import (
|
|
40
|
+
IMAGE_INDEX,
|
|
41
|
+
IMAGE_NAME,
|
|
42
|
+
IMAGE_PATH,
|
|
43
|
+
TIER_A_EXCLUDE_FROM_PROPERTIES,
|
|
44
|
+
append_property_columns,
|
|
45
|
+
is_per_image_df,
|
|
46
|
+
per_image_dataframe,
|
|
47
|
+
reorder_tier_a_columns,
|
|
48
|
+
)
|
|
49
|
+
from .math import (
|
|
50
|
+
build_combined_features,
|
|
51
|
+
clean_correlation_inputs,
|
|
52
|
+
compute_distance_correlation,
|
|
53
|
+
compute_entropy,
|
|
54
|
+
compute_lasso_interpretability,
|
|
55
|
+
compute_mutual_info,
|
|
56
|
+
compute_pearson_correlation,
|
|
57
|
+
compute_rf_interpretability,
|
|
58
|
+
compute_spearman_correlation,
|
|
59
|
+
run_dimensionality_reduction,
|
|
60
|
+
run_dimensionality_reduction_matrix,
|
|
61
|
+
)
|
|
62
|
+
from .pipeline_results import PhenoMeResults
|
|
63
|
+
from .property_utils import metadata_to_stable_key, optimize_property_types
|
|
64
|
+
from .protocols import PhenoMeProtocol
|
|
65
|
+
from .results_export import build_export_dataframe, prepare_embedding_dataframe
|
|
66
|
+
from .results_metadata import (
|
|
67
|
+
build_metadata_columns,
|
|
68
|
+
filter_indices,
|
|
69
|
+
get_all_metadata_keys,
|
|
70
|
+
get_metadata_value,
|
|
71
|
+
get_metadata_value_from_dict,
|
|
72
|
+
)
|
|
73
|
+
from .results_validation import validate_results
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
"IMAGE_INDEX",
|
|
77
|
+
"IMAGE_NAME",
|
|
78
|
+
"IMAGE_PATH",
|
|
79
|
+
"TIER_A_EXCLUDE_FROM_PROPERTIES",
|
|
80
|
+
"PhenoMeProtocol",
|
|
81
|
+
"PhenoMeResults",
|
|
82
|
+
"append_property_columns",
|
|
83
|
+
"build_combined_features",
|
|
84
|
+
"build_export_dataframe",
|
|
85
|
+
"build_metadata_columns",
|
|
86
|
+
"clean_correlation_inputs",
|
|
87
|
+
"compute_distance_correlation",
|
|
88
|
+
"compute_entropy",
|
|
89
|
+
"compute_lasso_interpretability",
|
|
90
|
+
"compute_mutual_info",
|
|
91
|
+
"compute_pearson_correlation",
|
|
92
|
+
"compute_rf_interpretability",
|
|
93
|
+
"compute_spearman_correlation",
|
|
94
|
+
"filter_indices",
|
|
95
|
+
"get_all_metadata_keys",
|
|
96
|
+
"get_metadata_value",
|
|
97
|
+
"get_metadata_value_from_dict",
|
|
98
|
+
"is_per_image_df",
|
|
99
|
+
"metadata_to_stable_key",
|
|
100
|
+
"optimize_property_types",
|
|
101
|
+
"per_image_dataframe",
|
|
102
|
+
"prepare_embedding_dataframe",
|
|
103
|
+
"reorder_tier_a_columns",
|
|
104
|
+
"run_dimensionality_reduction",
|
|
105
|
+
"run_dimensionality_reduction_matrix",
|
|
106
|
+
"validate_results",
|
|
107
|
+
]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Slim title/detail step payloads for checkpoint persistence.
|
|
2
|
+
|
|
3
|
+
Kept separate from ``processing_pipeline`` so HDF5 I/O can slim steps without
|
|
4
|
+
importing image preflight (which would cycle through ``phenome.io``).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
__all__ = ["coalesce_pipeline_payload", "slim_pipeline_payload"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _printed_step_kind(step: Mapping[str, Any]) -> str | None:
|
|
16
|
+
"""Return a persisted pipeline kind, or None when the step has none."""
|
|
17
|
+
kind = str(step.get("kind") or "").strip().lower()
|
|
18
|
+
if kind:
|
|
19
|
+
return kind
|
|
20
|
+
if str(step.get("id") or "").startswith("mask_"):
|
|
21
|
+
return "mask"
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _is_mask_step(step: Mapping[str, Any]) -> bool:
|
|
26
|
+
"""Return True when *step* is a mask-only pipeline step."""
|
|
27
|
+
return _printed_step_kind(step) == "mask"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _printed_step_fields(step: Any) -> dict[str, str] | None:
|
|
31
|
+
"""Keep title, detail, and kind for printing or persistence."""
|
|
32
|
+
if not isinstance(step, Mapping):
|
|
33
|
+
return None
|
|
34
|
+
title = str(step.get("title") or "").strip()
|
|
35
|
+
detail = str(step.get("detail") or "").strip()
|
|
36
|
+
if not title and not detail:
|
|
37
|
+
return None
|
|
38
|
+
out: dict[str, str] = {}
|
|
39
|
+
if title:
|
|
40
|
+
out["title"] = title
|
|
41
|
+
if detail:
|
|
42
|
+
out["detail"] = detail
|
|
43
|
+
kind = _printed_step_kind(step)
|
|
44
|
+
if kind:
|
|
45
|
+
out["kind"] = kind
|
|
46
|
+
return out
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _steps_from_value(value: Any) -> list[dict[str, str]] | None:
|
|
50
|
+
"""Extract ordered title/detail steps from a slim list or a full segment."""
|
|
51
|
+
raw: Any = value
|
|
52
|
+
if isinstance(value, Mapping) and "steps" in value:
|
|
53
|
+
raw = value.get("steps")
|
|
54
|
+
if not isinstance(raw, list):
|
|
55
|
+
return None
|
|
56
|
+
kept = [step for item in raw if (step := _printed_step_fields(item))]
|
|
57
|
+
return kept or None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def slim_pipeline_payload(payload: Mapping[str, Any] | None) -> dict[str, Any] | None:
|
|
61
|
+
"""Return only embeddings/properties step lists suitable for HDF5.
|
|
62
|
+
|
|
63
|
+
Accepts a session dict, a slim ``{embeddings, properties}`` payload, a
|
|
64
|
+
full segment with ``steps``, or a bare step list.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
payload: Session pipeline, slim checkpoint payload, or None.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
``{embeddings, properties}`` lists of printed step fields, or None.
|
|
71
|
+
"""
|
|
72
|
+
if not payload:
|
|
73
|
+
return None
|
|
74
|
+
embeddings = _steps_from_value(payload.get("embeddings"))
|
|
75
|
+
if embeddings is None:
|
|
76
|
+
embeddings = _steps_from_value(payload.get("base"))
|
|
77
|
+
properties = _steps_from_value(payload.get("properties"))
|
|
78
|
+
if embeddings is None and properties is None:
|
|
79
|
+
embeddings = _steps_from_value(payload)
|
|
80
|
+
out: dict[str, Any] = {}
|
|
81
|
+
if embeddings:
|
|
82
|
+
out["embeddings"] = embeddings
|
|
83
|
+
if properties:
|
|
84
|
+
out["properties"] = properties
|
|
85
|
+
return out or None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def coalesce_pipeline_payload(
|
|
89
|
+
incoming: Mapping[str, Any] | None,
|
|
90
|
+
existing: Mapping[str, Any] | None,
|
|
91
|
+
) -> dict[str, Any] | None:
|
|
92
|
+
"""Fill missing embeddings/properties steps from *existing*.
|
|
93
|
+
|
|
94
|
+
Used when a write has only one segment (for example ``process_images``
|
|
95
|
+
after ``reset()``) so the other stored segment is not deleted.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
incoming: Session pipeline or slim payload being written.
|
|
99
|
+
existing: Payload already stored on the checkpoint, or None.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Merged slim payload, or None when both sides are empty.
|
|
103
|
+
"""
|
|
104
|
+
slim_in = slim_pipeline_payload(incoming)
|
|
105
|
+
slim_ex = slim_pipeline_payload(existing)
|
|
106
|
+
if slim_in is None:
|
|
107
|
+
return slim_ex
|
|
108
|
+
if slim_ex is None:
|
|
109
|
+
return slim_in
|
|
110
|
+
merged = dict(slim_ex)
|
|
111
|
+
merged.update(slim_in)
|
|
112
|
+
return merged
|