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
@@ -0,0 +1,92 @@
1
+ import pandas as pd
2
+ from pandas import DataFrame
3
+ from polars import DataFrame as PolarsDataFrame
4
+ from polars import LazyFrame
5
+
6
+ from ngio.tables.backends._abstract_backend import AbstractTableBackend
7
+ from ngio.tables.backends._utils import (
8
+ normalize_pandas_df,
9
+ normalize_polars_lf,
10
+ )
11
+ from ngio.utils import NgioError
12
+
13
+
14
+ class JsonTableBackend(AbstractTableBackend):
15
+ """A class to load and write small tables in the zarr group .attrs (json)."""
16
+
17
+ @staticmethod
18
+ def backend_name() -> str:
19
+ """Return the name of the backend."""
20
+ return "json"
21
+
22
+ @staticmethod
23
+ def implements_anndata() -> bool:
24
+ """Whether the handler implements the anndata protocol."""
25
+ return False
26
+
27
+ @staticmethod
28
+ def implements_pandas() -> bool:
29
+ """Whether the handler implements the dataframe protocol."""
30
+ return True
31
+
32
+ @staticmethod
33
+ def implements_polars() -> bool:
34
+ """Whether the handler implements the polars protocol."""
35
+ return True
36
+
37
+ def _get_table_group(self):
38
+ """Get the table group, creating it if it doesn't exist."""
39
+ try:
40
+ table_group = self._group_handler.get_group(path="table", create_mode=True)
41
+ except NgioError as e:
42
+ raise NgioError(
43
+ "Could not get or create a 'table' group in the store "
44
+ f"{self._group_handler.store} path "
45
+ f"{self._group_handler.group.path}/table."
46
+ ) from e
47
+ return table_group
48
+
49
+ def load_as_pandas_df(self) -> DataFrame:
50
+ """Load the table as a pandas DataFrame."""
51
+ table_dict = self._get_table_group().attrs.asdict()
52
+ data_frame = pd.DataFrame.from_dict(table_dict)
53
+ data_frame = normalize_pandas_df(
54
+ data_frame,
55
+ index_key=self.index_key,
56
+ index_type=self.index_type,
57
+ reset_index=False,
58
+ )
59
+ return data_frame
60
+
61
+ def load(self) -> DataFrame:
62
+ return self.load_as_pandas_df()
63
+
64
+ def _write_from_dict(self, table: dict) -> None:
65
+ """Write the table from a dictionary to the store."""
66
+ table_group = self._get_table_group()
67
+ table_group.attrs.clear()
68
+ table_group.attrs.update(table)
69
+
70
+ def write_from_pandas(self, table: DataFrame) -> None:
71
+ """Write the table from a pandas DataFrame."""
72
+ table = normalize_pandas_df(
73
+ table,
74
+ index_key=self.index_key,
75
+ index_type=self.index_type,
76
+ reset_index=True,
77
+ )
78
+ table_dict = table.to_dict(orient="list")
79
+ self._write_from_dict(table=table_dict)
80
+
81
+ def write_from_polars(self, table: PolarsDataFrame | LazyFrame) -> None:
82
+ """Write the table from a polars DataFrame or LazyFrame."""
83
+ table = normalize_polars_lf(
84
+ table,
85
+ index_key=self.index_key,
86
+ index_type=self.index_type,
87
+ )
88
+ if isinstance(table, LazyFrame):
89
+ table = table.collect()
90
+
91
+ table_dict = table.to_dict(as_series=False)
92
+ self._write_from_dict(table=table_dict)
@@ -0,0 +1,19 @@
1
+ from ngio.tables.backends._py_arrow_backends import PyArrowBackend
2
+
3
+
4
+ class ParquetTableBackend(PyArrowBackend):
5
+ """A class to load and write small tables in Parquet format."""
6
+
7
+ def __init__(
8
+ self,
9
+ ):
10
+ """Initialize the ParquetTableBackend."""
11
+ super().__init__(
12
+ table_name="table.parquet",
13
+ table_format="parquet",
14
+ )
15
+
16
+ @staticmethod
17
+ def backend_name() -> str:
18
+ """Return the name of the backend."""
19
+ return "parquet"
@@ -0,0 +1,222 @@
1
+ from typing import Literal
2
+
3
+ import polars as pl
4
+ import pyarrow as pa
5
+ import pyarrow.csv as pa_csv
6
+ import pyarrow.dataset as pa_ds
7
+ import pyarrow.fs as pa_fs
8
+ import pyarrow.parquet as pa_parquet
9
+ from pandas import DataFrame
10
+ from polars import DataFrame as PolarsDataFrame
11
+ from polars import LazyFrame
12
+ from zarr.storage import FsspecStore, LocalStore, MemoryStore, ZipStore
13
+
14
+ from ngio.tables.backends._abstract_backend import AbstractTableBackend
15
+ from ngio.tables.backends._utils import normalize_pandas_df, normalize_polars_lf
16
+ from ngio.utils import NgioValueError
17
+ from ngio.utils._zarr_utils import _make_sync_fs
18
+
19
+
20
+ class PyArrowBackend(AbstractTableBackend):
21
+ """A class to load and write small tables in CSV format."""
22
+
23
+ def __init__(
24
+ self,
25
+ table_name: str,
26
+ table_format: Literal["csv", "parquet"] = "parquet",
27
+ ):
28
+ self.table_name = table_name
29
+ self.table_format = table_format
30
+
31
+ @staticmethod
32
+ def implements_anndata() -> bool:
33
+ """Whether the handler implements the anndata protocol."""
34
+ return False
35
+
36
+ @staticmethod
37
+ def implements_pandas() -> bool:
38
+ """Whether the handler implements the dataframe protocol."""
39
+ return True
40
+
41
+ @staticmethod
42
+ def implements_polars() -> bool:
43
+ """Whether the handler implements the polars protocol."""
44
+ return True
45
+
46
+ @staticmethod
47
+ def backend_name() -> str:
48
+ """Return the name of the backend."""
49
+ raise NotImplementedError(
50
+ "The backend_name method must be implemented in the subclass."
51
+ )
52
+
53
+ def _raise_store_type_not_supported(self):
54
+ """Raise an error for unsupported store types."""
55
+ ext = self.table_name.split(".")[-1]
56
+ store = self._group_handler.store
57
+ raise NgioValueError(
58
+ f"Ngio does not support reading a {ext} table from a "
59
+ f"store of type {type(store)}. "
60
+ "Please make sure to use a compatible "
61
+ "store like a LocalStore, or "
62
+ "FsspecStore, or MemoryStore, or ZipStore."
63
+ )
64
+
65
+ def _load_from_local_store(self, store: LocalStore, path: str) -> pa_ds.Dataset:
66
+ """Load the table from a directory store."""
67
+ root_path = store.root
68
+ table_path = f"{root_path}/{path}/{self.table_name}"
69
+ dataset = pa_ds.dataset(table_path, format=self.table_format)
70
+ return dataset
71
+
72
+ def _load_from_fsspec_store(self, store: FsspecStore, path: str) -> pa_ds.Dataset:
73
+ """Load the table from an FS store."""
74
+ table_path = f"{store.path}/{path}/{self.table_name}"
75
+ fs = _make_sync_fs(store.fs)
76
+ dataset = pa_ds.dataset(table_path, format=self.table_format, filesystem=fs)
77
+ return dataset
78
+
79
+ def _load_from_in_memory_store(
80
+ self, store: MemoryStore, path: str
81
+ ) -> pa_ds.Dataset:
82
+ """Load the table from an in-memory store."""
83
+ table_path = f"{path}/{self.table_name}"
84
+ table = store._store_dict.get(table_path, None)
85
+ if table is None:
86
+ raise NgioValueError(
87
+ f"Table {self.table_name} not found in the in-memory store at "
88
+ f"path {path}."
89
+ )
90
+ assert isinstance(table, pa.Table)
91
+ dataset = pa_ds.dataset(table)
92
+ return dataset
93
+
94
+ def _load_from_zip_store(self, store: ZipStore, path: str) -> pa_ds.Dataset:
95
+ """Load the table from a zip store."""
96
+ raise NotImplementedError("Zip store loading is not implemented yet.")
97
+
98
+ def _load_pyarrow_dataset(self) -> pa_ds.Dataset:
99
+ """Load the table as a pyarrow Dataset."""
100
+ store = self._group_handler.store
101
+ path = self._group_handler.group.path
102
+ if isinstance(store, LocalStore):
103
+ return self._load_from_local_store(store, path)
104
+ elif isinstance(store, FsspecStore):
105
+ return self._load_from_fsspec_store(store, path)
106
+ elif isinstance(store, MemoryStore):
107
+ return self._load_from_in_memory_store(store, path)
108
+ elif isinstance(store, ZipStore):
109
+ return self._load_from_zip_store(store, path)
110
+ self._raise_store_type_not_supported()
111
+
112
+ def load_as_pandas_df(self) -> DataFrame:
113
+ """Load the table as a pandas DataFrame."""
114
+ dataset = self._load_pyarrow_dataset()
115
+ dataframe = dataset.to_table().to_pandas()
116
+ dataframe = normalize_pandas_df(
117
+ dataframe,
118
+ index_key=self.index_key,
119
+ index_type=self.index_type,
120
+ reset_index=False,
121
+ )
122
+ return dataframe
123
+
124
+ def load(self) -> DataFrame:
125
+ """Load the table as a pandas DataFrame."""
126
+ return self.load_as_pandas_df()
127
+
128
+ def load_as_polars_lf(self) -> LazyFrame:
129
+ """Load the table as a polars LazyFrame."""
130
+ dataset = self._load_pyarrow_dataset()
131
+ lazy_frame = pl.scan_pyarrow_dataset(dataset)
132
+ if not isinstance(lazy_frame, LazyFrame):
133
+ raise NgioValueError(
134
+ "Table is not a lazy frame. Please report this issue as an ngio bug."
135
+ f" {type(lazy_frame)}"
136
+ )
137
+
138
+ lazy_frame = normalize_polars_lf(
139
+ lazy_frame,
140
+ index_key=self.index_key,
141
+ index_type=self.index_type,
142
+ )
143
+ return lazy_frame
144
+
145
+ def _write_to_stream(self, stream, table: pa.Table) -> None:
146
+ """Write the table to a stream."""
147
+ if self.table_format == "parquet":
148
+ pa_parquet.write_table(table, stream)
149
+ elif self.table_format == "csv":
150
+ pa_csv.write_csv(table, stream)
151
+ else:
152
+ raise NgioValueError(
153
+ f"Unsupported table format: {self.table_format}. "
154
+ "Supported formats are 'parquet' and 'csv'."
155
+ )
156
+
157
+ def _write_to_local_store(
158
+ self, store: LocalStore, path: str, table: pa.Table
159
+ ) -> None:
160
+ """Write the table to a directory store."""
161
+ root_path = store.root
162
+ table_path = f"{root_path}/{path}/{self.table_name}"
163
+ self._write_to_stream(table_path, table)
164
+
165
+ def _write_to_fsspec_store(
166
+ self, store: FsspecStore, path: str, table: pa.Table
167
+ ) -> None:
168
+ """Write the table to an FS store."""
169
+ table_path = f"{store.path}/{path}/{self.table_name}"
170
+ fs = _make_sync_fs(store.fs)
171
+ fs = pa_fs.PyFileSystem(pa_fs.FSSpecHandler(fs))
172
+ with fs.open_output_stream(table_path) as out_stream:
173
+ self._write_to_stream(out_stream, table)
174
+
175
+ def _write_to_in_memory_store(
176
+ self, store: MemoryStore, path: str, table: pa.Table
177
+ ) -> None:
178
+ """Write the table to an in-memory store."""
179
+ table_path = f"{path}/{self.table_name}"
180
+ store._store_dict[table_path] = table
181
+
182
+ def _write_to_zip_store(self, store: ZipStore, path: str, table: pa.Table) -> None:
183
+ """Write the table to a zip store."""
184
+ raise NotImplementedError("Writing to zip store is not implemented yet.")
185
+
186
+ def _write_pyarrow_dataset(self, dataset: pa.Table) -> None:
187
+ """Write the table from a pyarrow Dataset."""
188
+ store = self._group_handler.store
189
+ path = self._group_handler.group.path
190
+ if isinstance(store, LocalStore):
191
+ return self._write_to_local_store(store=store, path=path, table=dataset)
192
+ elif isinstance(store, FsspecStore):
193
+ return self._write_to_fsspec_store(store=store, path=path, table=dataset)
194
+ elif isinstance(store, MemoryStore):
195
+ return self._write_to_in_memory_store(store=store, path=path, table=dataset)
196
+ elif isinstance(store, ZipStore):
197
+ return self._write_to_zip_store(store=store, path=path, table=dataset)
198
+ self._raise_store_type_not_supported()
199
+
200
+ def write_from_pandas(self, table: DataFrame) -> None:
201
+ """Write the table from a pandas DataFrame."""
202
+ table = normalize_pandas_df(
203
+ table,
204
+ index_key=self.index_key,
205
+ index_type=self.index_type,
206
+ reset_index=True,
207
+ )
208
+ table = pa.Table.from_pandas(table, preserve_index=False)
209
+ self._write_pyarrow_dataset(table)
210
+
211
+ def write_from_polars(self, table: PolarsDataFrame | LazyFrame) -> None:
212
+ """Write the table from a polars DataFrame or LazyFrame."""
213
+ table = normalize_polars_lf(
214
+ table,
215
+ index_key=self.index_key,
216
+ index_type=self.index_type,
217
+ )
218
+
219
+ if isinstance(table, LazyFrame):
220
+ table = table.collect()
221
+ table = table.to_arrow()
222
+ self._write_pyarrow_dataset(table)
@@ -1,48 +1,147 @@
1
1
  """Protocol for table backends handlers."""
2
2
 
3
- from collections.abc import Collection
4
3
  from typing import Literal, Protocol
5
4
 
6
5
  from anndata import AnnData
7
6
  from pandas import DataFrame
8
-
9
- from ngio.tables.backends._anndata_v1 import AnnDataBackend
10
- from ngio.tables.backends._json_v1 import JsonTableBackend
7
+ from polars import DataFrame as PolarsDataFrame
8
+ from polars import LazyFrame
9
+
10
+ from ngio.tables.backends._anndata import AnnDataBackend, AnnDataBackendV1
11
+ from ngio.tables.backends._csv import CsvTableBackend
12
+ from ngio.tables.backends._json import JsonTableBackend
13
+ from ngio.tables.backends._parquet import ParquetTableBackend
14
+ from ngio.tables.backends._utils import TabularData
11
15
  from ngio.utils import NgioValueError, ZarrGroupHandler
12
16
 
13
17
 
14
18
  class TableBackendProtocol(Protocol):
15
- def __init__(
19
+ def set_group_handler(
16
20
  self,
17
21
  group_handler: ZarrGroupHandler,
18
22
  index_key: str | None = None,
19
- index_type: Literal["int", "str"] = "int",
20
- ): ...
23
+ index_type: Literal["int", "str"] | None = None,
24
+ ) -> None:
25
+ """Attach a group handler to the backend.
26
+
27
+ Index keys and index types are used to ensure that the
28
+ serialization and deserialization of the table
29
+ is consistent across different backends.
30
+
31
+ Making sure that this is consistent is
32
+ a duty of the backend implementations.
33
+ """
34
+ ...
21
35
 
22
36
  @staticmethod
23
- def backend_name() -> str: ...
37
+ def backend_name() -> str:
38
+ """Return the name of the backend.
39
+
40
+ As a convention we set name as:
41
+ {backend_name}_v{version}
42
+
43
+ Where the version is a integer.
44
+ """
45
+ ...
46
+
47
+ @property
48
+ def group_handler(self) -> ZarrGroupHandler:
49
+ """Return the group handler."""
50
+ ...
24
51
 
25
52
  @staticmethod
26
- def implements_anndata() -> bool: ...
53
+ def implements_anndata() -> bool:
54
+ """Check if the backend implements the anndata protocol.
55
+
56
+ If this is True, the backend should implement the
57
+ `write_from_anndata` method.
58
+
59
+ AnnData objects are more complex than DataFrames,
60
+ so if this is true the backend should implement the
61
+ full serialization of the AnnData object.
62
+
63
+ If this is False, these methods should raise a
64
+ `NotImplementedError`.
65
+ """
66
+ ...
67
+
68
+ @staticmethod
69
+ def implements_pandas() -> bool:
70
+ """Check if the backend implements the pandas protocol.
71
+
72
+ If this is True, the backend should implement the
73
+ `write_from_dataframe` methods.
74
+
75
+ If this is False, these methods should raise a
76
+ `NotImplementedError`.
77
+ """
78
+ ...
27
79
 
28
80
  @staticmethod
29
- def implements_dataframe() -> bool: ...
81
+ def implements_polars() -> bool:
82
+ """Check if the backend implements the polars protocol.
83
+
84
+ If this is True, the backend should implement the
85
+ `write_from_polars` methods.
86
+
87
+ If this is False, these methods should raise a
88
+ `NotImplementedError`.
89
+ """
90
+ ...
91
+
92
+ def load_as_anndata(self) -> AnnData:
93
+ """Load the table as an AnnData object."""
94
+ ...
30
95
 
31
- def load_columns(self) -> list[str]: ...
96
+ def load_as_pandas_df(self) -> DataFrame:
97
+ """Load the table as a pandas DataFrame."""
98
+ ...
32
99
 
33
- def load_as_anndata(self, columns: Collection[str] | None = None) -> AnnData: ...
100
+ def load_as_polars_lf(self) -> LazyFrame:
101
+ """Load the table as a polars LazyFrame."""
102
+ ...
34
103
 
35
- def load_as_dataframe(
36
- self, columns: Collection[str] | None = None
37
- ) -> DataFrame: ...
104
+ def load(self) -> TabularData:
105
+ """The default load method.
38
106
 
39
- def write_from_dataframe(
40
- self, table: DataFrame, metadata: dict | None = None
41
- ) -> None: ...
107
+ This method will be default way to load the table
108
+ from the backend. This method should wrap one of the
109
+ `load_as_anndata`, `load_as_dataframe` or `load_as_polars`
110
+ methods depending on the backend implementation.
111
+ """
112
+ ...
42
113
 
43
- def write_from_anndata(
44
- self, table: AnnData, metadata: dict | None = None
45
- ) -> None: ...
114
+ def write_from_pandas(self, table: DataFrame) -> None:
115
+ """Serialize the table from a pandas DataFrame."""
116
+ ...
117
+
118
+ def write_from_anndata(self, table: AnnData) -> None:
119
+ """Serialize the table from an AnnData object."""
120
+ ...
121
+
122
+ def write_from_polars(self, table: LazyFrame | PolarsDataFrame) -> None:
123
+ """Serialize the table from a polars DataFrame or LazyFrame."""
124
+ ...
125
+
126
+ def write(
127
+ self,
128
+ table_data: DataFrame | AnnData | PolarsDataFrame | LazyFrame,
129
+ metadata: dict[str, str] | None = None,
130
+ ) -> None:
131
+ """This is a generic write method.
132
+
133
+ Will call the appropriate write method
134
+ depending on the type of the table.
135
+
136
+ Moreover it will also write the metadata
137
+ if provided, and the backend methadata
138
+
139
+ the backend should write in the zarr group attributes
140
+ - backend: the backend name (self.backend_name())
141
+ - index_key: the index key
142
+ - index_type: the index type
143
+
144
+ """
46
145
 
47
146
 
48
147
  class ImplementedTableBackends:
@@ -65,38 +164,63 @@ class ImplementedTableBackends:
65
164
 
66
165
  def get_backend(
67
166
  self,
68
- backend_name: str | None,
167
+ *,
69
168
  group_handler: ZarrGroupHandler,
169
+ backend_name: str = "anndata",
70
170
  index_key: str | None = None,
71
- index_type: Literal["int", "str"] = "int",
171
+ index_type: Literal["int", "str"] | None = None,
72
172
  ) -> TableBackendProtocol:
73
173
  """Try to get a handler for the given store based on the metadata version."""
74
- if backend_name is None:
75
- # Default to anndata since it is currently
76
- # the only backend in use.
77
- backend_name = "anndata_v1"
78
-
79
174
  if backend_name not in self._implemented_backends:
80
175
  raise NgioValueError(f"Table backend {backend_name} not implemented.")
81
- handler = self._implemented_backends[backend_name](
176
+ backend = self._implemented_backends[backend_name]()
177
+ backend.set_group_handler(
82
178
  group_handler=group_handler, index_key=index_key, index_type=index_type
83
179
  )
84
- return handler
180
+ return backend
85
181
 
86
- def add_backend(
182
+ def _add_backend(
87
183
  self,
88
- table_beckend: type[TableBackendProtocol],
184
+ table_backend: type[TableBackendProtocol],
185
+ name: str,
89
186
  overwrite: bool = False,
90
- ):
187
+ ) -> None:
91
188
  """Register a new handler."""
92
- backend_name = table_beckend.backend_name()
93
- if backend_name in self._implemented_backends and not overwrite:
189
+ if name in self._implemented_backends and not overwrite:
94
190
  raise NgioValueError(
95
- f"Table backend {backend_name} already implemented. "
191
+ f"Table backend {name} already implemented. "
96
192
  "Use the `overwrite=True` parameter to overwrite it."
97
193
  )
98
- self._implemented_backends[backend_name] = table_beckend
194
+ self._implemented_backends[name] = table_backend
195
+
196
+ def add_backend(
197
+ self,
198
+ table_backend: type[TableBackendProtocol],
199
+ overwrite: bool = False,
200
+ aliases: list[str] | None = None,
201
+ ) -> None:
202
+ """Register a new handler."""
203
+ self._add_backend(
204
+ table_backend=table_backend,
205
+ name=table_backend.backend_name(),
206
+ overwrite=overwrite,
207
+ )
208
+ if aliases is not None:
209
+ for alias in aliases:
210
+ self._add_backend(
211
+ table_backend=table_backend, name=alias, overwrite=overwrite
212
+ )
99
213
 
100
214
 
101
215
  ImplementedTableBackends().add_backend(AnnDataBackend)
102
- ImplementedTableBackends().add_backend(JsonTableBackend)
216
+ ImplementedTableBackends().add_backend(AnnDataBackendV1)
217
+ ImplementedTableBackends().add_backend(
218
+ JsonTableBackend, aliases=["experimental_json_v1"]
219
+ )
220
+ ImplementedTableBackends().add_backend(CsvTableBackend, aliases=["experimental_csv_v1"])
221
+ ImplementedTableBackends().add_backend(
222
+ ParquetTableBackend, aliases=["experimental_parquet_v1"]
223
+ )
224
+
225
+ TableBackend = Literal["anndata", "json", "csv", "parquet"] | str | TableBackendProtocol
226
+ DefaultTableBackend = "anndata_v1"