ngio 0.2.0a2__py3-none-any.whl → 0.5.0b4__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.
- ngio/__init__.py +40 -12
- ngio/common/__init__.py +16 -32
- ngio/common/_dimensions.py +270 -48
- ngio/common/_masking_roi.py +153 -0
- ngio/common/_pyramid.py +267 -73
- ngio/common/_roi.py +290 -66
- ngio/common/_synt_images_utils.py +101 -0
- ngio/common/_zoom.py +54 -22
- ngio/experimental/__init__.py +5 -0
- ngio/experimental/iterators/__init__.py +15 -0
- ngio/experimental/iterators/_abstract_iterator.py +390 -0
- ngio/experimental/iterators/_feature.py +189 -0
- ngio/experimental/iterators/_image_processing.py +130 -0
- ngio/experimental/iterators/_mappers.py +48 -0
- ngio/experimental/iterators/_rois_utils.py +126 -0
- ngio/experimental/iterators/_segmentation.py +235 -0
- ngio/hcs/__init__.py +17 -58
- ngio/hcs/_plate.py +1354 -0
- ngio/images/__init__.py +30 -9
- ngio/images/_abstract_image.py +968 -0
- ngio/images/_create_synt_container.py +132 -0
- ngio/images/_create_utils.py +423 -0
- ngio/images/_image.py +926 -0
- ngio/images/_label.py +417 -0
- ngio/images/_masked_image.py +531 -0
- ngio/images/_ome_zarr_container.py +1235 -0
- ngio/images/_table_ops.py +471 -0
- ngio/io_pipes/__init__.py +75 -0
- ngio/io_pipes/_io_pipes.py +361 -0
- ngio/io_pipes/_io_pipes_masked.py +488 -0
- ngio/io_pipes/_io_pipes_roi.py +146 -0
- ngio/io_pipes/_io_pipes_types.py +56 -0
- ngio/io_pipes/_match_shape.py +377 -0
- ngio/io_pipes/_ops_axes.py +344 -0
- ngio/io_pipes/_ops_slices.py +411 -0
- ngio/io_pipes/_ops_slices_utils.py +199 -0
- ngio/io_pipes/_ops_transforms.py +104 -0
- ngio/io_pipes/_zoom_transform.py +180 -0
- ngio/ome_zarr_meta/__init__.py +39 -15
- ngio/ome_zarr_meta/_meta_handlers.py +490 -96
- ngio/ome_zarr_meta/ngio_specs/__init__.py +24 -10
- ngio/ome_zarr_meta/ngio_specs/_axes.py +268 -234
- ngio/ome_zarr_meta/ngio_specs/_channels.py +125 -41
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +42 -87
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +536 -2
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +202 -198
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +72 -34
- ngio/ome_zarr_meta/v04/__init__.py +21 -5
- ngio/ome_zarr_meta/v04/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +151 -90
- ngio/ome_zarr_meta/v05/__init__.py +27 -0
- ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v05/_v05_spec.py +511 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
- ngio/resources/__init__.py +55 -0
- ngio/resources/resource_model.py +36 -0
- ngio/tables/__init__.py +20 -4
- ngio/tables/_abstract_table.py +270 -0
- ngio/tables/_tables_container.py +449 -0
- ngio/tables/backends/__init__.py +50 -1
- ngio/tables/backends/_abstract_backend.py +200 -31
- ngio/tables/backends/_anndata.py +139 -0
- ngio/tables/backends/_anndata_utils.py +10 -114
- ngio/tables/backends/_csv.py +19 -0
- ngio/tables/backends/_json.py +92 -0
- ngio/tables/backends/_parquet.py +19 -0
- ngio/tables/backends/_py_arrow_backends.py +222 -0
- ngio/tables/backends/_table_backends.py +162 -38
- ngio/tables/backends/_utils.py +608 -0
- ngio/tables/v1/__init__.py +19 -4
- ngio/tables/v1/_condition_table.py +71 -0
- ngio/tables/v1/_feature_table.py +79 -115
- ngio/tables/v1/_generic_table.py +21 -90
- ngio/tables/v1/_roi_table.py +486 -137
- ngio/transforms/__init__.py +5 -0
- ngio/transforms/_zoom.py +19 -0
- ngio/utils/__init__.py +16 -14
- ngio/utils/_cache.py +48 -0
- ngio/utils/_datasets.py +121 -13
- ngio/utils/_fractal_fsspec_store.py +42 -0
- ngio/utils/_zarr_utils.py +374 -218
- ngio-0.5.0b4.dist-info/METADATA +147 -0
- ngio-0.5.0b4.dist-info/RECORD +88 -0
- {ngio-0.2.0a2.dist-info → ngio-0.5.0b4.dist-info}/WHEEL +1 -1
- ngio/common/_array_pipe.py +0 -160
- ngio/common/_axes_transforms.py +0 -63
- ngio/common/_common_types.py +0 -5
- ngio/common/_slicer.py +0 -97
- ngio/images/abstract_image.py +0 -240
- ngio/images/create.py +0 -251
- ngio/images/image.py +0 -389
- ngio/images/label.py +0 -236
- ngio/images/omezarr_container.py +0 -535
- ngio/ome_zarr_meta/_generic_handlers.py +0 -320
- ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -54
- ngio/tables/_validators.py +0 -192
- ngio/tables/backends/_anndata_v1.py +0 -75
- ngio/tables/backends/_json_v1.py +0 -56
- ngio/tables/tables_container.py +0 -300
- ngio/tables/v1/_masking_roi_table.py +0 -175
- ngio/utils/_logger.py +0 -29
- ngio-0.2.0a2.dist-info/METADATA +0 -95
- ngio-0.2.0a2.dist-info/RECORD +0 -53
- {ngio-0.2.0a2.dist-info → ngio-0.5.0b4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
"""Base class for handling OME-NGFF metadata in Zarr groups."""
|
|
2
|
-
|
|
3
|
-
from typing import Generic, Protocol, TypeVar
|
|
4
|
-
|
|
5
|
-
from pydantic import ValidationError
|
|
6
|
-
|
|
7
|
-
from ngio.ome_zarr_meta.ngio_specs import AxesSetup, NgioImageMeta, NgioLabelMeta
|
|
8
|
-
from ngio.utils import (
|
|
9
|
-
NgioValueError,
|
|
10
|
-
ZarrGroupHandler,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
ConverterError = ValidationError | Exception | None
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ImageMetaHandler(Protocol):
|
|
17
|
-
"""Protocol for OME-Zarr image handlers."""
|
|
18
|
-
|
|
19
|
-
def __init__(
|
|
20
|
-
self,
|
|
21
|
-
group_handler: ZarrGroupHandler,
|
|
22
|
-
axes_setup: AxesSetup | None = None,
|
|
23
|
-
allow_non_canonical_axes: bool = False,
|
|
24
|
-
strict_canonical_order: bool = True,
|
|
25
|
-
):
|
|
26
|
-
"""Initialize the handler."""
|
|
27
|
-
...
|
|
28
|
-
|
|
29
|
-
def safe_load_meta(self) -> NgioImageMeta | ConverterError:
|
|
30
|
-
"""Load the metadata from the store."""
|
|
31
|
-
...
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def meta(self) -> NgioImageMeta:
|
|
35
|
-
"""Return the metadata."""
|
|
36
|
-
...
|
|
37
|
-
|
|
38
|
-
def write_meta(self, meta: NgioImageMeta) -> None:
|
|
39
|
-
"""Write the metadata to the store."""
|
|
40
|
-
...
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class LabelMetaHandler(Protocol):
|
|
44
|
-
"""Protocol for OME-Zarr label handlers."""
|
|
45
|
-
|
|
46
|
-
def __init__(
|
|
47
|
-
self,
|
|
48
|
-
group_handler: ZarrGroupHandler,
|
|
49
|
-
axes_setup: AxesSetup | None = None,
|
|
50
|
-
allow_non_canonical_axes: bool = False,
|
|
51
|
-
strict_canonical_order: bool = True,
|
|
52
|
-
):
|
|
53
|
-
"""Initialize the handler."""
|
|
54
|
-
...
|
|
55
|
-
|
|
56
|
-
def safe_load_meta(self) -> NgioLabelMeta | ConverterError:
|
|
57
|
-
"""Load the metadata from the store."""
|
|
58
|
-
...
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def meta(self) -> NgioLabelMeta:
|
|
62
|
-
"""Return the metadata."""
|
|
63
|
-
...
|
|
64
|
-
|
|
65
|
-
def write_meta(self, meta: NgioLabelMeta) -> None:
|
|
66
|
-
"""Write the metadata to the store."""
|
|
67
|
-
...
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
###########################################################################
|
|
71
|
-
#
|
|
72
|
-
# The code below implements a generic class for handling OME-Zarr metadata
|
|
73
|
-
# in Zarr groups.
|
|
74
|
-
#
|
|
75
|
-
###########################################################################
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class ImageMetaImporter(Protocol):
|
|
79
|
-
@staticmethod
|
|
80
|
-
def __call__(
|
|
81
|
-
metadata: dict,
|
|
82
|
-
axes_setup: AxesSetup | None = None,
|
|
83
|
-
allow_non_canonical_axes: bool = False,
|
|
84
|
-
strict_canonical_order: bool = True,
|
|
85
|
-
) -> tuple[bool, NgioImageMeta | ConverterError]:
|
|
86
|
-
"""Convert the metadata to a NgioImageMeta object.
|
|
87
|
-
|
|
88
|
-
Args:
|
|
89
|
-
metadata (dict): The metadata (typically from a Zarr group .attrs).
|
|
90
|
-
axes_setup (AxesSetup, optional): The axes setup.
|
|
91
|
-
This is used to map axes with non-canonical names.
|
|
92
|
-
allow_non_canonical_axes (bool, optional): Whether to allow non-canonical
|
|
93
|
-
axes.
|
|
94
|
-
strict_canonical_order (bool, optional): Whether to enforce a strict
|
|
95
|
-
canonical order.
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
tuple[bool, NgioImageMeta | ConverterError]: A tuple with a boolean
|
|
99
|
-
indicating whether the conversion was successful and the
|
|
100
|
-
NgioImageMeta object or an error.
|
|
101
|
-
|
|
102
|
-
"""
|
|
103
|
-
...
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class ImageMetaExporter(Protocol):
|
|
107
|
-
def __call__(self, metadata: NgioImageMeta) -> dict: ...
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class LabelMetaImporter(Protocol):
|
|
111
|
-
@staticmethod
|
|
112
|
-
def __call__(
|
|
113
|
-
metadata: dict,
|
|
114
|
-
axes_setup: AxesSetup | None = None,
|
|
115
|
-
allow_non_canonical_axes: bool = False,
|
|
116
|
-
strict_canonical_order: bool = True,
|
|
117
|
-
) -> tuple[bool, NgioLabelMeta | ConverterError]:
|
|
118
|
-
"""Convert the metadata to a NgioLabelMeta object.
|
|
119
|
-
|
|
120
|
-
Args:
|
|
121
|
-
metadata (dict): The metadata (typically from a Zarr group .attrs).
|
|
122
|
-
axes_setup (AxesSetup, optional): The axes setup.
|
|
123
|
-
This is used to map axes with non-canonical names.
|
|
124
|
-
allow_non_canonical_axes (bool, optional): Whether to allow non-canonical
|
|
125
|
-
axes.
|
|
126
|
-
strict_canonical_order (bool, optional): Whether to enforce a strict
|
|
127
|
-
canonical order.
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
tuple[bool, NgioLabelMeta | ConverterError]: A tuple with a boolean
|
|
131
|
-
indicating whether the conversion was successful and the
|
|
132
|
-
NgioLabelMeta object or an error.
|
|
133
|
-
|
|
134
|
-
"""
|
|
135
|
-
...
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class LabelMetaExporter(Protocol):
|
|
139
|
-
def __call__(self, metadata: NgioLabelMeta) -> dict: ...
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
_meta = TypeVar("_meta", NgioImageMeta, NgioLabelMeta)
|
|
143
|
-
_meta_importer = TypeVar("_meta_importer", ImageMetaImporter, LabelMetaImporter)
|
|
144
|
-
_meta_exporter = TypeVar("_meta_exporter", ImageMetaExporter, LabelMetaExporter)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
class GenericMetaHandler(Generic[_meta, _meta_importer, _meta_exporter]):
|
|
148
|
-
"""Generic class for handling OME-Zarr metadata in Zarr groups."""
|
|
149
|
-
|
|
150
|
-
def __init__(
|
|
151
|
-
self,
|
|
152
|
-
meta_importer: _meta_importer,
|
|
153
|
-
meta_exporter: _meta_exporter,
|
|
154
|
-
group_handler: ZarrGroupHandler,
|
|
155
|
-
axes_setup: AxesSetup | None = None,
|
|
156
|
-
allow_non_canonical_axes: bool = False,
|
|
157
|
-
strict_canonical_order: bool = True,
|
|
158
|
-
):
|
|
159
|
-
"""Initialize the handler.
|
|
160
|
-
|
|
161
|
-
Args:
|
|
162
|
-
meta_importer (MetaImporter): The metadata importer.
|
|
163
|
-
meta_exporter (MetaExporter): The metadata exporter.
|
|
164
|
-
group_handler (ZarrGroupHandler): The Zarr group handler.
|
|
165
|
-
axes_setup (AxesSetup, optional): The axes setup.
|
|
166
|
-
This is used to map axes with non-canonical names.
|
|
167
|
-
allow_non_canonical_axes (bool, optional): Whether to allow non-canonical
|
|
168
|
-
axes.
|
|
169
|
-
strict_canonical_order (bool, optional): Whether to enforce a strict
|
|
170
|
-
canonical order.
|
|
171
|
-
"""
|
|
172
|
-
self._group_handler = group_handler
|
|
173
|
-
self._meta_importer = meta_importer
|
|
174
|
-
self._meta_exporter = meta_exporter
|
|
175
|
-
self._axes_setup = axes_setup
|
|
176
|
-
self._allow_non_canonical_axes = allow_non_canonical_axes
|
|
177
|
-
self._strict_canonical_order = strict_canonical_order
|
|
178
|
-
|
|
179
|
-
def _load_meta(self, return_error: bool = False):
|
|
180
|
-
"""Load the metadata from the store."""
|
|
181
|
-
attrs = self._group_handler.load_attrs()
|
|
182
|
-
is_valid, meta_or_error = self._meta_importer(
|
|
183
|
-
metadata=attrs,
|
|
184
|
-
axes_setup=self._axes_setup,
|
|
185
|
-
allow_non_canonical_axes=self._allow_non_canonical_axes,
|
|
186
|
-
strict_canonical_order=self._strict_canonical_order,
|
|
187
|
-
)
|
|
188
|
-
if is_valid:
|
|
189
|
-
return meta_or_error
|
|
190
|
-
|
|
191
|
-
if return_error:
|
|
192
|
-
return meta_or_error
|
|
193
|
-
|
|
194
|
-
raise NgioValueError(f"Could not load metadata: {meta_or_error}")
|
|
195
|
-
|
|
196
|
-
def safe_load_meta(self) -> _meta | ConverterError:
|
|
197
|
-
"""Load the metadata from the store."""
|
|
198
|
-
return self._load_meta(return_error=True)
|
|
199
|
-
|
|
200
|
-
def _write_meta(self, meta) -> None:
|
|
201
|
-
"""Write the metadata to the store."""
|
|
202
|
-
v04_meta = self._meta_exporter(metadata=meta)
|
|
203
|
-
self._group_handler.write_attrs(v04_meta)
|
|
204
|
-
|
|
205
|
-
def write_meta(self, meta: _meta) -> None:
|
|
206
|
-
"""Write the metadata to the store."""
|
|
207
|
-
raise NotImplementedError
|
|
208
|
-
|
|
209
|
-
@property
|
|
210
|
-
def meta(self) -> _meta:
|
|
211
|
-
"""Return the metadata."""
|
|
212
|
-
raise NotImplementedError
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
class BaseImageMetaHandler(
|
|
216
|
-
GenericMetaHandler[NgioImageMeta, ImageMetaImporter, ImageMetaExporter]
|
|
217
|
-
):
|
|
218
|
-
"""Generic class for handling OME-Zarr metadata in Zarr groups."""
|
|
219
|
-
|
|
220
|
-
def __init__(
|
|
221
|
-
self,
|
|
222
|
-
meta_importer: ImageMetaImporter,
|
|
223
|
-
meta_exporter: ImageMetaExporter,
|
|
224
|
-
group_handler: ZarrGroupHandler,
|
|
225
|
-
axes_setup: AxesSetup | None = None,
|
|
226
|
-
allow_non_canonical_axes: bool = False,
|
|
227
|
-
strict_canonical_order: bool = True,
|
|
228
|
-
):
|
|
229
|
-
"""Initialize the handler.
|
|
230
|
-
|
|
231
|
-
Args:
|
|
232
|
-
meta_importer (ImageMetaImporter): The metadata importer.
|
|
233
|
-
meta_exporter (ImageMetaExporter): The metadata exporter.
|
|
234
|
-
group_handler (ZarrGroupHandler): The Zarr group handler.
|
|
235
|
-
axes_setup (AxesSetup, optional): The axes setup.
|
|
236
|
-
This is used to map axes with non-canonical names.
|
|
237
|
-
allow_non_canonical_axes (bool, optional): Whether to allow non-canonical
|
|
238
|
-
axes.
|
|
239
|
-
strict_canonical_order (bool, optional): Whether to enforce a strict
|
|
240
|
-
canonical order.
|
|
241
|
-
"""
|
|
242
|
-
super().__init__(
|
|
243
|
-
meta_importer=meta_importer,
|
|
244
|
-
meta_exporter=meta_exporter,
|
|
245
|
-
group_handler=group_handler,
|
|
246
|
-
axes_setup=axes_setup,
|
|
247
|
-
allow_non_canonical_axes=allow_non_canonical_axes,
|
|
248
|
-
strict_canonical_order=strict_canonical_order,
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
def safe_load_meta(
|
|
252
|
-
self, return_error: bool = False
|
|
253
|
-
) -> NgioImageMeta | ConverterError:
|
|
254
|
-
"""Load the metadata from the store."""
|
|
255
|
-
return self._load_meta(return_error)
|
|
256
|
-
|
|
257
|
-
@property
|
|
258
|
-
def meta(self) -> NgioImageMeta:
|
|
259
|
-
"""Load the metadata from the store."""
|
|
260
|
-
meta = self._load_meta()
|
|
261
|
-
if isinstance(meta, NgioImageMeta):
|
|
262
|
-
return meta
|
|
263
|
-
raise NgioValueError(f"Could not load metadata: {meta}")
|
|
264
|
-
|
|
265
|
-
def write_meta(self, meta: NgioImageMeta) -> None:
|
|
266
|
-
self._write_meta(meta)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
class BaseLabelMetaHandler(
|
|
270
|
-
GenericMetaHandler[NgioLabelMeta, LabelMetaImporter, LabelMetaExporter]
|
|
271
|
-
):
|
|
272
|
-
"""Generic class for handling OME-Zarr metadata in Zarr groups."""
|
|
273
|
-
|
|
274
|
-
def __init__(
|
|
275
|
-
self,
|
|
276
|
-
meta_importer: LabelMetaImporter,
|
|
277
|
-
meta_exporter: LabelMetaExporter,
|
|
278
|
-
group_handler: ZarrGroupHandler,
|
|
279
|
-
axes_setup: AxesSetup | None = None,
|
|
280
|
-
allow_non_canonical_axes: bool = False,
|
|
281
|
-
strict_canonical_order: bool = True,
|
|
282
|
-
):
|
|
283
|
-
"""Initialize the handler.
|
|
284
|
-
|
|
285
|
-
Args:
|
|
286
|
-
meta_importer (LabelMetaImporter): The metadata importer.
|
|
287
|
-
meta_exporter (LabelMetaExporter): The metadata exporter.
|
|
288
|
-
group_handler (ZarrGroupHandler): The Zarr group handler.
|
|
289
|
-
axes_setup (AxesSetup, optional): The axes setup.
|
|
290
|
-
This is used to map axes with non-canonical names.
|
|
291
|
-
allow_non_canonical_axes (bool, optional): Whether to allow non-canonical
|
|
292
|
-
axes.
|
|
293
|
-
strict_canonical_order (bool, optional): Whether to enforce a strict
|
|
294
|
-
canonical order.
|
|
295
|
-
"""
|
|
296
|
-
super().__init__(
|
|
297
|
-
meta_importer=meta_importer,
|
|
298
|
-
meta_exporter=meta_exporter,
|
|
299
|
-
group_handler=group_handler,
|
|
300
|
-
axes_setup=axes_setup,
|
|
301
|
-
allow_non_canonical_axes=allow_non_canonical_axes,
|
|
302
|
-
strict_canonical_order=strict_canonical_order,
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
def safe_load_meta(
|
|
306
|
-
self, return_error: bool = False
|
|
307
|
-
) -> NgioLabelMeta | ConverterError:
|
|
308
|
-
"""Load the metadata from the store."""
|
|
309
|
-
return self._load_meta(return_error)
|
|
310
|
-
|
|
311
|
-
@property
|
|
312
|
-
def meta(self) -> NgioLabelMeta:
|
|
313
|
-
"""Load the metadata from the store."""
|
|
314
|
-
meta = self._load_meta()
|
|
315
|
-
if isinstance(meta, NgioLabelMeta):
|
|
316
|
-
return meta
|
|
317
|
-
raise NgioValueError(f"Could not load metadata: {meta}")
|
|
318
|
-
|
|
319
|
-
def write_meta(self, meta: NgioLabelMeta) -> None:
|
|
320
|
-
self._write_meta(meta)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"""Concrete implementation of the OME-Zarr metadata handlers for version 0.4."""
|
|
2
|
-
|
|
3
|
-
from ngio.ome_zarr_meta._generic_handlers import (
|
|
4
|
-
BaseImageMetaHandler,
|
|
5
|
-
BaseLabelMetaHandler,
|
|
6
|
-
)
|
|
7
|
-
from ngio.ome_zarr_meta.ngio_specs import AxesSetup
|
|
8
|
-
from ngio.ome_zarr_meta.v04._v04_spec_utils import (
|
|
9
|
-
ngio_to_v04_image_meta,
|
|
10
|
-
ngio_to_v04_label_meta,
|
|
11
|
-
v04_to_ngio_image_meta,
|
|
12
|
-
v04_to_ngio_label_meta,
|
|
13
|
-
)
|
|
14
|
-
from ngio.utils import ZarrGroupHandler
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class V04ImageMetaHandler(BaseImageMetaHandler):
|
|
18
|
-
"""Base class for handling OME-Zarr 0.4 metadata."""
|
|
19
|
-
|
|
20
|
-
def __init__(
|
|
21
|
-
self,
|
|
22
|
-
group_handler: ZarrGroupHandler,
|
|
23
|
-
axes_setup: AxesSetup | None = None,
|
|
24
|
-
allow_non_canonical_axes: bool = False,
|
|
25
|
-
strict_canonical_order: bool = True,
|
|
26
|
-
):
|
|
27
|
-
super().__init__(
|
|
28
|
-
meta_importer=v04_to_ngio_image_meta,
|
|
29
|
-
meta_exporter=ngio_to_v04_image_meta,
|
|
30
|
-
group_handler=group_handler,
|
|
31
|
-
axes_setup=axes_setup,
|
|
32
|
-
allow_non_canonical_axes=allow_non_canonical_axes,
|
|
33
|
-
strict_canonical_order=strict_canonical_order,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class V04LabelMetaHandler(BaseLabelMetaHandler):
|
|
38
|
-
"""Base class for handling OME-Zarr 0.4 metadata."""
|
|
39
|
-
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
group_handler: ZarrGroupHandler,
|
|
43
|
-
axes_setup: AxesSetup | None = None,
|
|
44
|
-
allow_non_canonical_axes: bool = False,
|
|
45
|
-
strict_canonical_order: bool = True,
|
|
46
|
-
):
|
|
47
|
-
super().__init__(
|
|
48
|
-
meta_importer=v04_to_ngio_label_meta,
|
|
49
|
-
meta_exporter=ngio_to_v04_label_meta,
|
|
50
|
-
group_handler=group_handler,
|
|
51
|
-
axes_setup=axes_setup,
|
|
52
|
-
allow_non_canonical_axes=allow_non_canonical_axes,
|
|
53
|
-
strict_canonical_order=strict_canonical_order,
|
|
54
|
-
)
|
ngio/tables/_validators.py
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
from collections.abc import Iterable
|
|
2
|
-
from typing import Protocol
|
|
3
|
-
|
|
4
|
-
import pandas as pd
|
|
5
|
-
import pandas.api.types as ptypes
|
|
6
|
-
|
|
7
|
-
from ngio.utils import (
|
|
8
|
-
NgioTableValidationError,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TableValidator(Protocol):
|
|
13
|
-
def __call__(self, table: pd.DataFrame) -> pd.DataFrame:
|
|
14
|
-
"""Validate the table DataFrame.
|
|
15
|
-
|
|
16
|
-
A Validator is just a simple callable that takes a
|
|
17
|
-
DataFrame and returns a DataFrame.
|
|
18
|
-
|
|
19
|
-
If the DataFrame is valid, the same DataFrame is returned.
|
|
20
|
-
If the DataFrame is invalid, the Validator can either modify the DataFrame
|
|
21
|
-
to make it valid or raise a NgioTableValidationError.
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
table (pd.DataFrame): The DataFrame to validate.
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
pd.DataFrame: The validated DataFrame.
|
|
28
|
-
|
|
29
|
-
"""
|
|
30
|
-
...
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def validate_table(
|
|
34
|
-
table_df: pd.DataFrame,
|
|
35
|
-
validators: Iterable[TableValidator] | None = None,
|
|
36
|
-
) -> pd.DataFrame:
|
|
37
|
-
"""Validate the table DataFrame.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
table_df (pd.DataFrame): The DataFrame to validate.
|
|
41
|
-
validators (Collection[Validator] | None): A collection of functions
|
|
42
|
-
used to validate the table. Default is None.
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
pd.DataFrame: The validated DataFrame.
|
|
46
|
-
"""
|
|
47
|
-
validators = validators or []
|
|
48
|
-
|
|
49
|
-
# Apply all provided validators
|
|
50
|
-
for validator in validators:
|
|
51
|
-
table_df = validator(table_df)
|
|
52
|
-
|
|
53
|
-
return table_df
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
####################################################################################################
|
|
57
|
-
#
|
|
58
|
-
# Common table validators
|
|
59
|
-
#
|
|
60
|
-
####################################################################################################
|
|
61
|
-
def validate_index_key(
|
|
62
|
-
dataframe: pd.DataFrame, index_key: str | None, overwrite: bool = False
|
|
63
|
-
) -> pd.DataFrame:
|
|
64
|
-
"""Correctly set the index of the DataFrame.
|
|
65
|
-
|
|
66
|
-
This function checks if the index_key is present in the DataFrame.
|
|
67
|
-
If not it tries to set sensible defaults.
|
|
68
|
-
|
|
69
|
-
In order:
|
|
70
|
-
- If index_key is None, nothing can be done.
|
|
71
|
-
- If index_key is already the index of the DataFrame, nothing is done.
|
|
72
|
-
- If index_key is in the columns, we set the index to that column.
|
|
73
|
-
- If current index is None, we set the index to the index_key.
|
|
74
|
-
- If current index is not None and overwrite is True,
|
|
75
|
-
we set the index to the index_key.
|
|
76
|
-
|
|
77
|
-
"""
|
|
78
|
-
if index_key is None:
|
|
79
|
-
# Nothing to do
|
|
80
|
-
return dataframe
|
|
81
|
-
|
|
82
|
-
if dataframe.index.name == index_key:
|
|
83
|
-
# Index is already set to index_key correctly
|
|
84
|
-
return dataframe
|
|
85
|
-
|
|
86
|
-
if index_key in dataframe.columns:
|
|
87
|
-
dataframe = dataframe.set_index(index_key)
|
|
88
|
-
return dataframe
|
|
89
|
-
|
|
90
|
-
if dataframe.index.name is None:
|
|
91
|
-
dataframe.index.name = index_key
|
|
92
|
-
return dataframe
|
|
93
|
-
|
|
94
|
-
elif overwrite:
|
|
95
|
-
dataframe.index.name = index_key
|
|
96
|
-
return dataframe
|
|
97
|
-
else:
|
|
98
|
-
raise NgioTableValidationError(
|
|
99
|
-
f"Index key {index_key} not found in DataFrame. "
|
|
100
|
-
f"Current index is {dataframe.index.name}. If you want to overwrite the "
|
|
101
|
-
"index set overwrite=True."
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def validate_index_dtype(dataframe: pd.DataFrame, index_type: str) -> pd.DataFrame:
|
|
106
|
-
"""Check if the index of the DataFrame has the correct dtype."""
|
|
107
|
-
match index_type:
|
|
108
|
-
case "str":
|
|
109
|
-
if ptypes.is_integer_dtype(dataframe.index):
|
|
110
|
-
# Convert the int index to string is generally safe
|
|
111
|
-
dataframe = dataframe.set_index(dataframe.index.astype(str))
|
|
112
|
-
|
|
113
|
-
if not ptypes.is_string_dtype(dataframe.index):
|
|
114
|
-
raise NgioTableValidationError(
|
|
115
|
-
f"Table index must be of string type, got {dataframe.index.dtype}"
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
case "int":
|
|
119
|
-
if ptypes.is_string_dtype(dataframe.index):
|
|
120
|
-
# Try to convert the string index to int
|
|
121
|
-
try:
|
|
122
|
-
dataframe = dataframe.set_index(dataframe.index.astype(int))
|
|
123
|
-
except ValueError as e:
|
|
124
|
-
if "invalid literal for int() with base 10" in str(e):
|
|
125
|
-
raise NgioTableValidationError(
|
|
126
|
-
"Table index must be of integer type, got str."
|
|
127
|
-
f" We tried implicit conversion and failed: {e}"
|
|
128
|
-
) from None
|
|
129
|
-
else:
|
|
130
|
-
raise e from e
|
|
131
|
-
|
|
132
|
-
if not ptypes.is_integer_dtype(dataframe.index):
|
|
133
|
-
raise NgioTableValidationError(
|
|
134
|
-
f"Table index must be of integer type, got {dataframe.index.dtype}"
|
|
135
|
-
)
|
|
136
|
-
case _:
|
|
137
|
-
raise ValueError(f"index_type {index_type} not recognized")
|
|
138
|
-
|
|
139
|
-
return dataframe
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def validate_columns(
|
|
143
|
-
table_df: pd.DataFrame,
|
|
144
|
-
required_columns: list[str],
|
|
145
|
-
optional_columns: list[str] | None = None,
|
|
146
|
-
) -> pd.DataFrame:
|
|
147
|
-
"""Validate the columns headers of the table.
|
|
148
|
-
|
|
149
|
-
If a required column is missing, a TableValidationError is raised.
|
|
150
|
-
If a list of optional columns is provided, only required and optional columns are
|
|
151
|
-
allowed in the table.
|
|
152
|
-
|
|
153
|
-
Args:
|
|
154
|
-
table_df (pd.DataFrame): The DataFrame to validate.
|
|
155
|
-
required_columns (list[str]): A list of required columns.
|
|
156
|
-
optional_columns (list[str] | None): A list of optional columns.
|
|
157
|
-
Default is None.
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
pd.DataFrame: The validated DataFrame.
|
|
161
|
-
"""
|
|
162
|
-
table_header = table_df.columns
|
|
163
|
-
for column in required_columns:
|
|
164
|
-
if column not in table_header:
|
|
165
|
-
raise NgioTableValidationError(
|
|
166
|
-
f"Could not find required column: {column} in the table"
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
if optional_columns is None:
|
|
170
|
-
return table_df
|
|
171
|
-
|
|
172
|
-
possible_columns = [*required_columns, *optional_columns]
|
|
173
|
-
for column in table_header:
|
|
174
|
-
if column not in possible_columns:
|
|
175
|
-
raise NgioTableValidationError(
|
|
176
|
-
f"Could not find column: {column} in the list of possible columns. ",
|
|
177
|
-
f"Possible columns are: {possible_columns}",
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
return table_df
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def validate_unique_index(table_df: pd.DataFrame) -> pd.DataFrame:
|
|
184
|
-
"""Validate that the index of the table is unique."""
|
|
185
|
-
if table_df.index.is_unique:
|
|
186
|
-
return table_df
|
|
187
|
-
|
|
188
|
-
# Find the duplicates
|
|
189
|
-
duplicates = table_df.index[table_df.index.duplicated()].tolist()
|
|
190
|
-
raise NgioTableValidationError(
|
|
191
|
-
f"Index of the table contains duplicates values. Duplicate: {duplicates}"
|
|
192
|
-
)
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
from collections.abc import Collection
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
from anndata import AnnData
|
|
5
|
-
from pandas import DataFrame
|
|
6
|
-
|
|
7
|
-
from ngio.tables.backends._abstract_backend import AbstractTableBackend
|
|
8
|
-
from ngio.tables.backends._anndata_utils import (
|
|
9
|
-
anndata_to_dataframe,
|
|
10
|
-
custom_read_zarr,
|
|
11
|
-
dataframe_to_anndata,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class AnnDataBackend(AbstractTableBackend):
|
|
16
|
-
"""A class to load and write tables from/to an AnnData object."""
|
|
17
|
-
|
|
18
|
-
@staticmethod
|
|
19
|
-
def backend_name() -> str:
|
|
20
|
-
"""The name of the backend."""
|
|
21
|
-
return "anndata_v1"
|
|
22
|
-
|
|
23
|
-
@staticmethod
|
|
24
|
-
def implements_anndata() -> bool:
|
|
25
|
-
"""Whether the handler implements the anndata protocol."""
|
|
26
|
-
return True
|
|
27
|
-
|
|
28
|
-
@staticmethod
|
|
29
|
-
def implements_dataframe() -> bool:
|
|
30
|
-
"""Whether the handler implements the dataframe protocol."""
|
|
31
|
-
return True
|
|
32
|
-
|
|
33
|
-
def load_columns(self) -> list[str]:
|
|
34
|
-
"""List all labels in the group."""
|
|
35
|
-
return list(self.load_as_dataframe().columns)
|
|
36
|
-
|
|
37
|
-
def load_as_anndata(self, columns: Collection[str] | None = None) -> AnnData:
|
|
38
|
-
"""Load the metadata in the store."""
|
|
39
|
-
anndata = custom_read_zarr(self._group_handler._group)
|
|
40
|
-
if columns is not None:
|
|
41
|
-
raise NotImplementedError(
|
|
42
|
-
"Selecting columns is not implemented for AnnData."
|
|
43
|
-
)
|
|
44
|
-
return anndata
|
|
45
|
-
|
|
46
|
-
def load_as_dataframe(self, columns: Collection[str] | None = None) -> DataFrame:
|
|
47
|
-
"""List all labels in the group."""
|
|
48
|
-
dataframe = anndata_to_dataframe(
|
|
49
|
-
self.load_as_anndata(),
|
|
50
|
-
index_key=self._index_key,
|
|
51
|
-
index_type=self._index_type,
|
|
52
|
-
)
|
|
53
|
-
if columns is not None:
|
|
54
|
-
dataframe = dataframe[columns]
|
|
55
|
-
return dataframe
|
|
56
|
-
|
|
57
|
-
def write_from_dataframe(
|
|
58
|
-
self, table: DataFrame, metadata: dict | None = None
|
|
59
|
-
) -> None:
|
|
60
|
-
"""Consolidate the metadata in the store."""
|
|
61
|
-
anndata = dataframe_to_anndata(table, index_key=self._index_key)
|
|
62
|
-
self.write_from_anndata(anndata, metadata)
|
|
63
|
-
|
|
64
|
-
def write_from_anndata(self, table: AnnData, metadata: dict | None = None) -> None:
|
|
65
|
-
"""Consolidate the metadata in the store."""
|
|
66
|
-
store = self._group_handler.store
|
|
67
|
-
if not isinstance(store, str | Path):
|
|
68
|
-
raise ValueError(
|
|
69
|
-
"To write an AnnData object the store must be a local path/str."
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
store = Path(store) / self._group_handler.group.path
|
|
73
|
-
table.write_zarr(store)
|
|
74
|
-
if metadata is not None:
|
|
75
|
-
self._group_handler.write_attrs(metadata)
|
ngio/tables/backends/_json_v1.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
from collections.abc import Collection
|
|
2
|
-
|
|
3
|
-
import pandas as pd
|
|
4
|
-
from pandas import DataFrame
|
|
5
|
-
|
|
6
|
-
from ngio.tables.backends._abstract_backend import AbstractTableBackend
|
|
7
|
-
from ngio.utils import NgioFileNotFoundError
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class JsonTableBackend(AbstractTableBackend):
|
|
11
|
-
"""A class to load and write small tables in the zarr group .attrs (json)."""
|
|
12
|
-
|
|
13
|
-
@staticmethod
|
|
14
|
-
def backend_name() -> str:
|
|
15
|
-
"""The name of the backend."""
|
|
16
|
-
return "json_v1"
|
|
17
|
-
|
|
18
|
-
@staticmethod
|
|
19
|
-
def implements_anndata() -> bool:
|
|
20
|
-
"""Whether the handler implements the anndata protocol."""
|
|
21
|
-
return False
|
|
22
|
-
|
|
23
|
-
@staticmethod
|
|
24
|
-
def implements_dataframe() -> bool:
|
|
25
|
-
"""Whether the handler implements the dataframe protocol."""
|
|
26
|
-
return True
|
|
27
|
-
|
|
28
|
-
def load_columns(self) -> list[str]:
|
|
29
|
-
"""List all labels in the group."""
|
|
30
|
-
return list(self.load_as_dataframe().columns)
|
|
31
|
-
|
|
32
|
-
def _get_table_group(self):
|
|
33
|
-
try:
|
|
34
|
-
table_group = self._group_handler.get_group(path="table")
|
|
35
|
-
except NgioFileNotFoundError:
|
|
36
|
-
table_group = self._group_handler.group.create_group("table")
|
|
37
|
-
return table_group
|
|
38
|
-
|
|
39
|
-
def load_as_dataframe(self, columns: Collection[str] | None = None) -> DataFrame:
|
|
40
|
-
"""List all labels in the group."""
|
|
41
|
-
table_group = self._get_table_group()
|
|
42
|
-
table_dict = dict(table_group.attrs)
|
|
43
|
-
data_frame = pd.DataFrame.from_dict(table_dict)
|
|
44
|
-
if columns is not None:
|
|
45
|
-
data_frame = data_frame[columns]
|
|
46
|
-
return data_frame
|
|
47
|
-
|
|
48
|
-
def write_from_dataframe(
|
|
49
|
-
self, table: DataFrame, metadata: dict | None = None
|
|
50
|
-
) -> None:
|
|
51
|
-
"""Consolidate the metadata in the store."""
|
|
52
|
-
table_group = self._get_table_group()
|
|
53
|
-
table_group.attrs.clear()
|
|
54
|
-
table_group.attrs.update(table.to_dict())
|
|
55
|
-
if metadata is not None:
|
|
56
|
-
self._group_handler.write_attrs(metadata)
|