ngio 0.4.8__py3-none-any.whl → 0.5.0a1__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.
@@ -0,0 +1,518 @@
1
+ """Utilities for OME-Zarr v05 specs.
2
+
3
+ This module provides a set of classes to internally handle the metadata
4
+ of the OME-Zarr v05 specification.
5
+
6
+ For Images and Labels implements the following functionalities:
7
+ - A function to find if a dict view of the metadata is a valid OME-Zarr v05 metadata.
8
+ - A function to convert a v05 image metadata to a ngio image metadata.
9
+ - A function to convert a ngio image metadata to a v05 image metadata.
10
+ """
11
+
12
+ from ome_zarr_models.common.omero import Channel as ChannelV05
13
+ from ome_zarr_models.common.omero import Omero as OmeroV05
14
+ from ome_zarr_models.common.omero import Window as WindowV05
15
+ from ome_zarr_models.v05.axes import Axis as AxisV05
16
+ from ome_zarr_models.v05.coordinate_transformations import VectorScale as VectorScaleV05
17
+ from ome_zarr_models.v05.coordinate_transformations import (
18
+ VectorTranslation as VectorTranslationV05,
19
+ )
20
+ from ome_zarr_models.v05.hcs import HCSAttrs as HCSAttrsV05
21
+ from ome_zarr_models.v05.image import ImageAttrs as ImageAttrsV05
22
+ from ome_zarr_models.v05.image_label import ImageLabelAttrs as LabelAttrsV05
23
+ from ome_zarr_models.v05.multiscales import Dataset as DatasetV05
24
+ from ome_zarr_models.v05.multiscales import Multiscale as MultiscaleV05
25
+ from ome_zarr_models.v05.multiscales import ValidTransform as ValidTransformV05
26
+ from pydantic import BaseModel, ValidationError
27
+
28
+ from ngio.ome_zarr_meta.ngio_specs import (
29
+ AxesHandler,
30
+ AxesSetup,
31
+ Axis,
32
+ AxisType,
33
+ Channel,
34
+ ChannelsMeta,
35
+ ChannelVisualisation,
36
+ Dataset,
37
+ ImageLabelSource,
38
+ NgioImageMeta,
39
+ NgioLabelMeta,
40
+ NgioPlateMeta,
41
+ NgioWellMeta,
42
+ default_channel_name,
43
+ )
44
+ from ngio.ome_zarr_meta.v05._custom_models import CustomWellAttrs as WellAttrsV05
45
+
46
+
47
+ class ImageV05AttrsWithOmero(ImageAttrsV05):
48
+ omero: OmeroV05 | None = None
49
+
50
+
51
+ class ImageV05WithOmero(BaseModel):
52
+ ome: ImageV05AttrsWithOmero
53
+
54
+
55
+ class ImageLabelV05(BaseModel):
56
+ ome: LabelAttrsV05
57
+
58
+
59
+ def _is_v05_image_meta(metadata: dict) -> ImageV05WithOmero | ValidationError:
60
+ """Check if the metadata is a valid OME-Zarr v05 metadata.
61
+
62
+ Args:
63
+ metadata (dict): The metadata to check.
64
+
65
+ Returns:
66
+ bool: True if the metadata is a valid OME-Zarr v05 metadata, False otherwise.
67
+ """
68
+ try:
69
+ return ImageV05WithOmero(**metadata)
70
+ except ValidationError as e:
71
+ return e
72
+
73
+
74
+ def _is_v05_label_meta(metadata: dict) -> ImageLabelV05 | ValidationError:
75
+ """Check if the metadata is a valid OME-Zarr v05 metadata.
76
+
77
+ Args:
78
+ metadata (dict): The metadata to check.
79
+
80
+ Returns:
81
+ bool: True if the metadata is a valid OME-Zarr v05 metadata, False otherwise.
82
+ """
83
+ try:
84
+ return ImageLabelV05(**metadata)
85
+ except ValidationError as e:
86
+ return e
87
+
88
+
89
+ def _v05_omero_to_channels(v05_omero: OmeroV05 | None) -> ChannelsMeta | None:
90
+ if v05_omero is None:
91
+ return None
92
+
93
+ ngio_channels = []
94
+ for idx, v05_channel in enumerate(v05_omero.channels):
95
+ channel_extra = v05_channel.model_extra
96
+
97
+ if channel_extra is None:
98
+ channel_extra = {}
99
+
100
+ if "label" in channel_extra:
101
+ label = channel_extra.pop("label")
102
+ else:
103
+ label = default_channel_name(idx)
104
+
105
+ if "wavelength_id" in channel_extra:
106
+ wavelength_id = channel_extra.pop("wavelength_id")
107
+ else:
108
+ wavelength_id = label
109
+
110
+ if "active" in channel_extra:
111
+ active = channel_extra.pop("active")
112
+ else:
113
+ active = True
114
+
115
+ channel_visualisation = ChannelVisualisation(
116
+ color=v05_channel.color,
117
+ start=v05_channel.window.start,
118
+ end=v05_channel.window.end,
119
+ min=v05_channel.window.min,
120
+ max=v05_channel.window.max,
121
+ active=active,
122
+ **channel_extra,
123
+ )
124
+
125
+ ngio_channels.append(
126
+ Channel(
127
+ label=label,
128
+ wavelength_id=wavelength_id,
129
+ channel_visualisation=channel_visualisation,
130
+ )
131
+ )
132
+
133
+ v05_omero_extra = v05_omero.model_extra if v05_omero.model_extra is not None else {}
134
+ return ChannelsMeta(channels=ngio_channels, **v05_omero_extra)
135
+
136
+
137
+ def _compute_scale_translation(
138
+ v05_transforms: ValidTransformV05,
139
+ scale: list[float],
140
+ translation: list[float],
141
+ ) -> tuple[list[float], list[float]]:
142
+ for v05_transform in v05_transforms:
143
+ if isinstance(v05_transform, VectorScaleV05):
144
+ scale = [t1 * t2 for t1, t2 in zip(scale, v05_transform.scale, strict=True)]
145
+
146
+ elif isinstance(v05_transform, VectorTranslationV05):
147
+ translation = [
148
+ t1 + t2
149
+ for t1, t2 in zip(translation, v05_transform.translation, strict=True)
150
+ ]
151
+ else:
152
+ raise NotImplementedError(
153
+ f"Coordinate transformation {v05_transform} is not supported."
154
+ )
155
+ return scale, translation
156
+
157
+
158
+ def _v05_to_ngio_datasets(
159
+ v05_multiscale: MultiscaleV05,
160
+ axes_setup: AxesSetup,
161
+ allow_non_canonical_axes: bool = False,
162
+ strict_canonical_order: bool = True,
163
+ ) -> list[Dataset]:
164
+ """Convert a v05 multiscale to a list of ngio datasets."""
165
+ datasets = []
166
+
167
+ global_scale = [1.0] * len(v05_multiscale.axes)
168
+ global_translation = [0.0] * len(v05_multiscale.axes)
169
+
170
+ if v05_multiscale.coordinateTransformations is not None:
171
+ global_scale, global_translation = _compute_scale_translation(
172
+ v05_multiscale.coordinateTransformations, global_scale, global_translation
173
+ )
174
+
175
+ # Prepare axes handler
176
+ axes = []
177
+ for v05_axis in v05_multiscale.axes:
178
+ unit = v05_axis.unit
179
+ if unit is not None and not isinstance(unit, str):
180
+ unit = str(unit)
181
+ axes.append(
182
+ Axis(
183
+ name=str(v05_axis.name),
184
+ axis_type=AxisType(v05_axis.type),
185
+ # (for some reason the type is a generic JsonValue,
186
+ # but it should be a string or None)
187
+ unit=v05_axis.unit, # type: ignore
188
+ )
189
+ )
190
+ axes_handler = AxesHandler(
191
+ axes=axes,
192
+ axes_setup=axes_setup,
193
+ allow_non_canonical_axes=allow_non_canonical_axes,
194
+ strict_canonical_order=strict_canonical_order,
195
+ )
196
+
197
+ for v05_dataset in v05_multiscale.datasets:
198
+ _scale, _translation = _compute_scale_translation(
199
+ v05_dataset.coordinateTransformations, global_scale, global_translation
200
+ )
201
+ datasets.append(
202
+ Dataset(
203
+ path=v05_dataset.path,
204
+ axes_handler=axes_handler,
205
+ scale=_scale,
206
+ translation=_translation,
207
+ )
208
+ )
209
+ return datasets
210
+
211
+
212
+ def v05_to_ngio_image_meta(
213
+ metadata: dict,
214
+ axes_setup: AxesSetup | None = None,
215
+ allow_non_canonical_axes: bool = False,
216
+ strict_canonical_order: bool = True,
217
+ ) -> tuple[bool, NgioImageMeta | ValidationError]:
218
+ """Convert a v05 image metadata to a ngio image metadata.
219
+
220
+ Args:
221
+ metadata (dict): The v05 image metadata.
222
+ axes_setup (AxesSetup, optional): The axes setup. This is
223
+ required to convert image with non-canonical axes names.
224
+ allow_non_canonical_axes (bool, optional): Allow non-canonical axes.
225
+ strict_canonical_order (bool, optional): Strict canonical order.
226
+
227
+ Returns:
228
+ NgioImageMeta: The ngio image metadata.
229
+ """
230
+ v05_image = _is_v05_image_meta(metadata)
231
+ if isinstance(v05_image, ValidationError):
232
+ return False, v05_image
233
+ v05_image = v05_image.ome
234
+ if len(v05_image.multiscales) > 1:
235
+ raise NotImplementedError(
236
+ "Multiple multiscales in a single image are not supported in ngio."
237
+ )
238
+
239
+ v05_multiscale = v05_image.multiscales[0]
240
+
241
+ channels_meta = _v05_omero_to_channels(v05_image.omero)
242
+ axes_setup = axes_setup if axes_setup is not None else AxesSetup()
243
+ datasets = _v05_to_ngio_datasets(
244
+ v05_multiscale,
245
+ axes_setup=axes_setup,
246
+ allow_non_canonical_axes=allow_non_canonical_axes,
247
+ strict_canonical_order=strict_canonical_order,
248
+ )
249
+
250
+ name = v05_multiscale.name
251
+ if name is not None and not isinstance(name, str):
252
+ name = str(name)
253
+ return True, NgioImageMeta(
254
+ version="0.5",
255
+ name=name,
256
+ datasets=datasets,
257
+ channels=channels_meta,
258
+ )
259
+
260
+
261
+ def v05_to_ngio_label_meta(
262
+ metadata: dict,
263
+ axes_setup: AxesSetup | None = None,
264
+ allow_non_canonical_axes: bool = False,
265
+ strict_canonical_order: bool = True,
266
+ ) -> tuple[bool, NgioLabelMeta | ValidationError]:
267
+ """Convert a v05 image metadata to a ngio image metadata.
268
+
269
+ Args:
270
+ metadata (dict): The v05 image metadata.
271
+ axes_setup (AxesSetup, optional): The axes setup. This is
272
+ required to convert image with non-canonical axes names.
273
+ allow_non_canonical_axes (bool, optional): Allow non-canonical axes.
274
+ strict_canonical_order (bool, optional): Strict canonical order.
275
+
276
+ Returns:
277
+ NgioImageMeta: The ngio image metadata.
278
+ """
279
+ v05_label = _is_v05_label_meta(metadata)
280
+ if isinstance(v05_label, ValidationError):
281
+ return False, v05_label
282
+ v05_label = v05_label.ome
283
+
284
+ if len(v05_label.multiscales) > 1:
285
+ raise NotImplementedError(
286
+ "Multiple multiscales in a single image are not supported in ngio."
287
+ )
288
+
289
+ v05_multiscale = v05_label.multiscales[0]
290
+
291
+ axes_setup = axes_setup if axes_setup is not None else AxesSetup()
292
+ datasets = _v05_to_ngio_datasets(
293
+ v05_multiscale,
294
+ axes_setup=axes_setup,
295
+ allow_non_canonical_axes=allow_non_canonical_axes,
296
+ strict_canonical_order=strict_canonical_order,
297
+ )
298
+
299
+ if v05_label.image_label is not None:
300
+ source = v05_label.image_label.source
301
+ if source is None:
302
+ image_label_source = None
303
+ else:
304
+ source = v05_label.image_label.source
305
+ if source is None:
306
+ image_label_source = None
307
+ else:
308
+ image_label_source = source.image
309
+ image_label_source = ImageLabelSource(
310
+ version="0.5",
311
+ source={"image": image_label_source},
312
+ )
313
+ else:
314
+ image_label_source = None
315
+ name = v05_multiscale.name
316
+ if name is not None and not isinstance(name, str):
317
+ name = str(name)
318
+
319
+ return True, NgioLabelMeta(
320
+ version="0.5",
321
+ name=name,
322
+ datasets=datasets,
323
+ image_label=image_label_source,
324
+ )
325
+
326
+
327
+ def _ngio_to_v05_multiscale(name: str | None, datasets: list[Dataset]) -> MultiscaleV05:
328
+ """Convert a ngio multiscale to a v05 multiscale.
329
+
330
+ Args:
331
+ name (str | None): The name of the multiscale.
332
+ datasets (list[Dataset]): The ngio datasets.
333
+
334
+ Returns:
335
+ MultiscaleV05: The v05 multiscale.
336
+ """
337
+ ax_mapper = datasets[0].axes_handler
338
+ v05_axes = []
339
+ for axis in ax_mapper.axes:
340
+ v05_axes.append(
341
+ AxisV05(
342
+ name=axis.name,
343
+ type=axis.axis_type.value if axis.axis_type is not None else None,
344
+ unit=axis.unit if axis.unit is not None else None,
345
+ )
346
+ )
347
+
348
+ v05_datasets = []
349
+ for dataset in datasets:
350
+ transform = [VectorScaleV05(type="scale", scale=list(dataset._scale))]
351
+ if sum(dataset._translation) > 0:
352
+ transform = (
353
+ VectorScaleV05(type="scale", scale=list(dataset._scale)),
354
+ VectorTranslationV05(
355
+ type="translation", translation=list(dataset._translation)
356
+ ),
357
+ )
358
+ else:
359
+ transform = (VectorScaleV05(type="scale", scale=list(dataset._scale)),)
360
+
361
+ v05_datasets.append(
362
+ DatasetV05(path=dataset.path, coordinateTransformations=transform)
363
+ )
364
+ return MultiscaleV05(axes=v05_axes, datasets=tuple(v05_datasets), name=name)
365
+
366
+
367
+ def _ngio_to_v05_omero(channels: ChannelsMeta | None) -> OmeroV05 | None:
368
+ """Convert a ngio channels to a v05 omero."""
369
+ if channels is None:
370
+ return None
371
+
372
+ v05_channels = []
373
+ for channel in channels.channels:
374
+ _model_extra = {
375
+ "label": channel.label,
376
+ "wavelength_id": channel.wavelength_id,
377
+ "active": channel.channel_visualisation.active,
378
+ }
379
+ if channel.channel_visualisation.model_extra is not None:
380
+ _model_extra.update(channel.channel_visualisation.model_extra)
381
+
382
+ v05_channels.append(
383
+ ChannelV05(
384
+ color=channel.channel_visualisation.valid_color,
385
+ window=WindowV05(
386
+ start=channel.channel_visualisation.start,
387
+ end=channel.channel_visualisation.end,
388
+ min=channel.channel_visualisation.min,
389
+ max=channel.channel_visualisation.max,
390
+ ),
391
+ **_model_extra,
392
+ )
393
+ )
394
+
395
+ _model_extra = channels.model_extra if channels.model_extra is not None else {}
396
+ return OmeroV05(channels=v05_channels, **_model_extra)
397
+
398
+
399
+ def ngio_to_v05_image_meta(metadata: NgioImageMeta) -> dict:
400
+ """Convert a ngio image metadata to a v05 image metadata.
401
+
402
+ Args:
403
+ metadata (NgioImageMeta): The ngio image metadata.
404
+
405
+ Returns:
406
+ dict: The v05 image metadata.
407
+ """
408
+ v05_muliscale = _ngio_to_v05_multiscale(
409
+ name=metadata.name, datasets=metadata.datasets
410
+ )
411
+ v05_omero = _ngio_to_v05_omero(metadata._channels_meta)
412
+
413
+ v05_image_attrs = ImageV05AttrsWithOmero(
414
+ multiscales=[v05_muliscale], omero=v05_omero, version="0.5"
415
+ )
416
+ v05_image = ImageV05WithOmero(
417
+ ome=v05_image_attrs,
418
+ )
419
+ return v05_image.model_dump(exclude_none=True, by_alias=True)
420
+
421
+
422
+ def ngio_to_v05_label_meta(metadata: NgioLabelMeta) -> dict:
423
+ """Convert a ngio image metadata to a v05 image metadata.
424
+
425
+ Args:
426
+ metadata (NgioImageMeta): The ngio image metadata.
427
+
428
+ Returns:
429
+ dict: The v05 image metadata.
430
+ """
431
+ v05_muliscale = _ngio_to_v05_multiscale(
432
+ name=metadata.name, datasets=metadata.datasets
433
+ )
434
+ labels_meta = {
435
+ "multiscales": [v05_muliscale],
436
+ "image-label": metadata.image_label.model_dump(),
437
+ }
438
+ v05_label = LabelAttrsV05(**labels_meta, version="0.5")
439
+ v05_label = ImageLabelV05(
440
+ ome=v05_label,
441
+ )
442
+ return v05_label.model_dump(exclude_none=True, by_alias=True)
443
+
444
+
445
+ class WellV05(BaseModel):
446
+ ome: WellAttrsV05
447
+
448
+
449
+ class HCSV05(BaseModel):
450
+ ome: HCSAttrsV05
451
+
452
+
453
+ def v05_to_ngio_well_meta(
454
+ metadata: dict,
455
+ ) -> tuple[bool, NgioWellMeta | ValidationError]:
456
+ """Convert a v05 well metadata to a ngio well metadata.
457
+
458
+ Args:
459
+ metadata (dict): The v05 well metadata.
460
+
461
+ Returns:
462
+ result (bool): True if the conversion was successful, False otherwise.
463
+ ngio_well_meta (NgioWellMeta): The ngio well metadata.
464
+ """
465
+ try:
466
+ v05_well = WellV05(**metadata)
467
+ except ValidationError as e:
468
+ return False, e
469
+
470
+ return True, NgioWellMeta(**v05_well.ome.model_dump())
471
+
472
+
473
+ def v05_to_ngio_plate_meta(
474
+ metadata: dict,
475
+ ) -> tuple[bool, NgioPlateMeta | ValidationError]:
476
+ """Convert a v05 plate metadata to a ngio plate metadata.
477
+
478
+ Args:
479
+ metadata (dict): The v05 plate metadata.
480
+
481
+ Returns:
482
+ result (bool): True if the conversion was successful, False otherwise.
483
+ ngio_plate_meta (NgioPlateMeta): The ngio plate metadata.
484
+ """
485
+ try:
486
+ v05_plate = HCSV05(**metadata)
487
+ except ValidationError as e:
488
+ return False, e
489
+
490
+ return True, NgioPlateMeta(**v05_plate.ome.model_dump())
491
+
492
+
493
+ def ngio_to_v05_well_meta(metadata: NgioWellMeta) -> dict:
494
+ """Convert a ngio well metadata to a v05 well metadata.
495
+
496
+ Args:
497
+ metadata (NgioWellMeta): The ngio well metadata.
498
+
499
+ Returns:
500
+ dict: The v05 well metadata.
501
+ """
502
+ v05_well = WellAttrsV05(**metadata.model_dump())
503
+ v05_well = WellV05(ome=v05_well)
504
+ return v05_well.model_dump(exclude_none=True, by_alias=True)
505
+
506
+
507
+ def ngio_to_v05_plate_meta(metadata: NgioPlateMeta) -> dict:
508
+ """Convert a ngio plate metadata to a v05 plate metadata.
509
+
510
+ Args:
511
+ metadata (NgioPlateMeta): The ngio plate metadata.
512
+
513
+ Returns:
514
+ dict: The v05 plate metadata.
515
+ """
516
+ v05_plate = HCSAttrsV05(**metadata.model_dump())
517
+ v05_plate = HCSV05(ome=v05_plate)
518
+ return v05_plate.model_dump(exclude_none=True, by_alias=True)
@@ -359,7 +359,7 @@ ImplementedTables().add_implementation(ConditionTableV1)
359
359
  def open_tables_container(
360
360
  store: StoreOrGroup,
361
361
  cache: bool = False,
362
- mode: AccessModeLiteral = "r+",
362
+ mode: AccessModeLiteral = "a",
363
363
  parallel_safe: bool = False,
364
364
  ) -> TablesContainer:
365
365
  """Open a table handler from a Zarr store."""
@@ -373,7 +373,7 @@ def open_table(
373
373
  store: StoreOrGroup,
374
374
  backend: TableBackend | None = None,
375
375
  cache: bool = False,
376
- mode: AccessModeLiteral = "r+",
376
+ mode: AccessModeLiteral = "a",
377
377
  parallel_safe: bool = False,
378
378
  ) -> Table:
379
379
  """Open a table from a Zarr store."""
@@ -391,7 +391,7 @@ def open_table_as(
391
391
  table_cls: type[TableType],
392
392
  backend: TableBackend | None = None,
393
393
  cache: bool = False,
394
- mode: AccessModeLiteral = "r+",
394
+ mode: AccessModeLiteral = "a",
395
395
  parallel_safe: bool = False,
396
396
  ) -> TableType:
397
397
  """Open a table from a Zarr store as a specific type."""
@@ -198,6 +198,13 @@ class AbstractTableBackend(ABC):
198
198
  if metadata is None:
199
199
  metadata = {}
200
200
 
201
+ attrs = self._group_handler.reopen_group().attrs.asdict()
202
+ # This is required by anndata to identify the format
203
+ if "encoding-type" in attrs:
204
+ metadata["encoding-type"] = attrs["encoding-type"]
205
+ if "encoding-version" in attrs:
206
+ metadata["encoding-version"] = attrs["encoding-version"]
207
+
201
208
  backend_metadata = BackendMeta(
202
209
  backend=self.backend_name(),
203
210
  index_key=self.index_key,
@@ -1,4 +1,5 @@
1
1
  from anndata import AnnData
2
+ from anndata._settings import settings
2
3
  from pandas import DataFrame
3
4
  from polars import DataFrame as PolarsDataFrame
4
5
  from polars import LazyFrame
@@ -40,6 +41,7 @@ class AnnDataBackend(AbstractTableBackend):
40
41
 
41
42
  def load_as_anndata(self) -> AnnData:
42
43
  """Load the table as an AnnData object."""
44
+ settings.zarr_write_format = self._group_handler.zarr_format
43
45
  anndata = custom_anndata_read_zarr(self._group_handler._group)
44
46
  anndata = normalize_anndata(anndata, index_key=self.index_key)
45
47
  return anndata
@@ -58,7 +60,8 @@ class AnnDataBackend(AbstractTableBackend):
58
60
  "Please make sure to use a compatible "
59
61
  "store like a zarr.DirectoryStore."
60
62
  )
61
- table.write_zarr(full_url) # type: ignore (AnnData writer requires a str path)
63
+ settings.zarr_write_format = self._group_handler.zarr_format
64
+ table.write_zarr(full_url)
62
65
 
63
66
  def write_from_pandas(self, table: DataFrame) -> None:
64
67
  """Serialize the table from a pandas DataFrame."""
@@ -9,6 +9,7 @@ from anndata._io.utils import _read_legacy_raw
9
9
  from anndata._io.zarr import read_dataframe
10
10
  from anndata.compat import _clean_uns
11
11
  from anndata.experimental import read_dispatched
12
+ from zarr.storage import LocalStore
12
13
 
13
14
  from ngio.utils import (
14
15
  NgioValueError,
@@ -35,7 +36,7 @@ def custom_anndata_read_zarr(
35
36
  """
36
37
  group = open_group_wrapper(store=store, mode="r")
37
38
 
38
- if not isinstance(group.store, zarr.DirectoryStore):
39
+ if not isinstance(group.store, LocalStore):
39
40
  elem_to_read = ["X", "obs", "var"]
40
41
 
41
42
  if elem_to_read is None:
@@ -87,6 +88,8 @@ def custom_anndata_read_zarr(
87
88
  if isinstance(group["obs"], zarr.Array):
88
89
  _clean_uns(adata)
89
90
 
91
+ if isinstance(adata, dict):
92
+ adata = AnnData(**adata)
90
93
  if not isinstance(adata, AnnData):
91
94
  raise NgioValueError(f"Expected an AnnData object, but got {type(adata)}")
92
95
  return adata
@@ -5,7 +5,7 @@ from typing import Any
5
5
  from pandas import DataFrame
6
6
  from polars import DataFrame as PolarsDataFrame
7
7
  from polars import LazyFrame
8
- from zarr.storage import DirectoryStore, FSStore
8
+ from zarr.storage import FsspecStore, LocalStore
9
9
 
10
10
  from ngio.tables.backends._abstract_backend import AbstractTableBackend
11
11
  from ngio.tables.backends._utils import normalize_pandas_df, normalize_polars_lf
@@ -88,9 +88,9 @@ class NonZarrBaseBackend(AbstractTableBackend):
88
88
  def load_as_pandas_df(self) -> DataFrame:
89
89
  """Load the table as a pandas DataFrame."""
90
90
  store = self._group_handler.store
91
- if isinstance(store, DirectoryStore):
91
+ if isinstance(store, LocalStore):
92
92
  dataframe = self._load_from_directory_store(reader=self.df_reader)
93
- elif isinstance(store, FSStore):
93
+ elif isinstance(store, FsspecStore):
94
94
  dataframe = self._load_from_fs_store_df(reader=self.df_reader)
95
95
  else:
96
96
  ext = self.table_name.split(".")[-1]
@@ -117,9 +117,9 @@ class NonZarrBaseBackend(AbstractTableBackend):
117
117
  def load_as_polars_lf(self) -> LazyFrame:
118
118
  """Load the table as a polars LazyFrame."""
119
119
  store = self._group_handler.store
120
- if isinstance(store, DirectoryStore):
120
+ if isinstance(store, LocalStore):
121
121
  lazy_frame = self._load_from_directory_store(reader=self.lf_reader)
122
- elif isinstance(store, FSStore):
122
+ elif isinstance(store, FsspecStore):
123
123
  lazy_frame = self._load_from_fs_store_lf(reader=self.lf_reader)
124
124
  else:
125
125
  ext = self.table_name.split(".")[-1]
@@ -146,7 +146,7 @@ class NonZarrBaseBackend(AbstractTableBackend):
146
146
  def _get_store_url(self) -> str:
147
147
  """Get the store URL."""
148
148
  store = self._group_handler.store
149
- if isinstance(store, DirectoryStore):
149
+ if isinstance(store, LocalStore):
150
150
  full_url = self._group_handler.full_url
151
151
  else:
152
152
  ext = self.table_name.split(".")[-1]
@@ -403,7 +403,7 @@ def convert_anndata_to_pandas(
403
403
  DataFrame: Converted and normalized pandas DataFrame.
404
404
  """
405
405
  pandas_df = anndata.to_df()
406
- pandas_df[anndata.obs_keys()] = anndata.obs
406
+ pandas_df[anndata.obs.columns.to_list()] = anndata.obs
407
407
  pandas_df = normalize_pandas_df(
408
408
  pandas_df,
409
409
  index_key=index_key,
@@ -86,10 +86,10 @@ def _dataframe_to_rois(
86
86
  ) -> dict[str, Roi]:
87
87
  """Convert a DataFrame to a WorldCooROI object."""
88
88
  # Validate the columns of the DataFrame
89
- _missing_columns = set(required_columns).difference(set(dataframe.columns))
90
- if len(_missing_columns) != 0:
89
+ _required_columns = set(dataframe.columns).intersection(set(required_columns))
90
+ if len(_required_columns) != len(required_columns):
91
91
  raise NgioTableValidationError(
92
- f"Could not find required columns: {_missing_columns} in the table."
92
+ f"Could not find required columns: {_required_columns} in the table."
93
93
  )
94
94
 
95
95
  extra_columns = set(dataframe.columns).difference(
ngio/utils/_datasets.py CHANGED
@@ -155,11 +155,5 @@ def download_ome_zarr_dataset(
155
155
  path=download_dir,
156
156
  processor=processor,
157
157
  progressbar=progressbar,
158
- # Add User-Agent to avoid 403 errors from Zenodo
159
- downloader=pooch.HTTPDownloader(
160
- headers={
161
- "User-Agent": f"pooch/{pooch.__version__} (https://github.com/BioVisionCenter/ngio)"
162
- }
163
- ),
164
158
  )
165
159
  return processor.output_file()