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.
Files changed (106) hide show
  1. ngio/__init__.py +40 -12
  2. ngio/common/__init__.py +16 -32
  3. ngio/common/_dimensions.py +270 -48
  4. ngio/common/_masking_roi.py +153 -0
  5. ngio/common/_pyramid.py +267 -73
  6. ngio/common/_roi.py +290 -66
  7. ngio/common/_synt_images_utils.py +101 -0
  8. ngio/common/_zoom.py +54 -22
  9. ngio/experimental/__init__.py +5 -0
  10. ngio/experimental/iterators/__init__.py +15 -0
  11. ngio/experimental/iterators/_abstract_iterator.py +390 -0
  12. ngio/experimental/iterators/_feature.py +189 -0
  13. ngio/experimental/iterators/_image_processing.py +130 -0
  14. ngio/experimental/iterators/_mappers.py +48 -0
  15. ngio/experimental/iterators/_rois_utils.py +126 -0
  16. ngio/experimental/iterators/_segmentation.py +235 -0
  17. ngio/hcs/__init__.py +17 -58
  18. ngio/hcs/_plate.py +1354 -0
  19. ngio/images/__init__.py +30 -9
  20. ngio/images/_abstract_image.py +968 -0
  21. ngio/images/_create_synt_container.py +132 -0
  22. ngio/images/_create_utils.py +423 -0
  23. ngio/images/_image.py +926 -0
  24. ngio/images/_label.py +417 -0
  25. ngio/images/_masked_image.py +531 -0
  26. ngio/images/_ome_zarr_container.py +1235 -0
  27. ngio/images/_table_ops.py +471 -0
  28. ngio/io_pipes/__init__.py +75 -0
  29. ngio/io_pipes/_io_pipes.py +361 -0
  30. ngio/io_pipes/_io_pipes_masked.py +488 -0
  31. ngio/io_pipes/_io_pipes_roi.py +146 -0
  32. ngio/io_pipes/_io_pipes_types.py +56 -0
  33. ngio/io_pipes/_match_shape.py +377 -0
  34. ngio/io_pipes/_ops_axes.py +344 -0
  35. ngio/io_pipes/_ops_slices.py +411 -0
  36. ngio/io_pipes/_ops_slices_utils.py +199 -0
  37. ngio/io_pipes/_ops_transforms.py +104 -0
  38. ngio/io_pipes/_zoom_transform.py +180 -0
  39. ngio/ome_zarr_meta/__init__.py +39 -15
  40. ngio/ome_zarr_meta/_meta_handlers.py +490 -96
  41. ngio/ome_zarr_meta/ngio_specs/__init__.py +24 -10
  42. ngio/ome_zarr_meta/ngio_specs/_axes.py +268 -234
  43. ngio/ome_zarr_meta/ngio_specs/_channels.py +125 -41
  44. ngio/ome_zarr_meta/ngio_specs/_dataset.py +42 -87
  45. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +536 -2
  46. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +202 -198
  47. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +72 -34
  48. ngio/ome_zarr_meta/v04/__init__.py +21 -5
  49. ngio/ome_zarr_meta/v04/_custom_models.py +18 -0
  50. ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +151 -90
  51. ngio/ome_zarr_meta/v05/__init__.py +27 -0
  52. ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
  53. ngio/ome_zarr_meta/v05/_v05_spec.py +511 -0
  54. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
  55. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  56. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
  57. ngio/resources/__init__.py +55 -0
  58. ngio/resources/resource_model.py +36 -0
  59. ngio/tables/__init__.py +20 -4
  60. ngio/tables/_abstract_table.py +270 -0
  61. ngio/tables/_tables_container.py +449 -0
  62. ngio/tables/backends/__init__.py +50 -1
  63. ngio/tables/backends/_abstract_backend.py +200 -31
  64. ngio/tables/backends/_anndata.py +139 -0
  65. ngio/tables/backends/_anndata_utils.py +10 -114
  66. ngio/tables/backends/_csv.py +19 -0
  67. ngio/tables/backends/_json.py +92 -0
  68. ngio/tables/backends/_parquet.py +19 -0
  69. ngio/tables/backends/_py_arrow_backends.py +222 -0
  70. ngio/tables/backends/_table_backends.py +162 -38
  71. ngio/tables/backends/_utils.py +608 -0
  72. ngio/tables/v1/__init__.py +19 -4
  73. ngio/tables/v1/_condition_table.py +71 -0
  74. ngio/tables/v1/_feature_table.py +79 -115
  75. ngio/tables/v1/_generic_table.py +21 -90
  76. ngio/tables/v1/_roi_table.py +486 -137
  77. ngio/transforms/__init__.py +5 -0
  78. ngio/transforms/_zoom.py +19 -0
  79. ngio/utils/__init__.py +16 -14
  80. ngio/utils/_cache.py +48 -0
  81. ngio/utils/_datasets.py +121 -13
  82. ngio/utils/_fractal_fsspec_store.py +42 -0
  83. ngio/utils/_zarr_utils.py +374 -218
  84. ngio-0.5.0b4.dist-info/METADATA +147 -0
  85. ngio-0.5.0b4.dist-info/RECORD +88 -0
  86. {ngio-0.2.0a2.dist-info → ngio-0.5.0b4.dist-info}/WHEEL +1 -1
  87. ngio/common/_array_pipe.py +0 -160
  88. ngio/common/_axes_transforms.py +0 -63
  89. ngio/common/_common_types.py +0 -5
  90. ngio/common/_slicer.py +0 -97
  91. ngio/images/abstract_image.py +0 -240
  92. ngio/images/create.py +0 -251
  93. ngio/images/image.py +0 -389
  94. ngio/images/label.py +0 -236
  95. ngio/images/omezarr_container.py +0 -535
  96. ngio/ome_zarr_meta/_generic_handlers.py +0 -320
  97. ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -54
  98. ngio/tables/_validators.py +0 -192
  99. ngio/tables/backends/_anndata_v1.py +0 -75
  100. ngio/tables/backends/_json_v1.py +0 -56
  101. ngio/tables/tables_container.py +0 -300
  102. ngio/tables/v1/_masking_roi_table.py +0 -175
  103. ngio/utils/_logger.py +0 -29
  104. ngio-0.2.0a2.dist-info/METADATA +0 -95
  105. ngio-0.2.0a2.dist-info/RECORD +0 -53
  106. {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
- )
@@ -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)
@@ -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)