ngio 0.2.9__py3-none-any.whl → 0.3.0__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 (38) hide show
  1. ngio/common/__init__.py +16 -0
  2. ngio/common/_array_pipe.py +50 -27
  3. ngio/common/_table_ops.py +471 -0
  4. ngio/hcs/__init__.py +1 -1
  5. ngio/hcs/{plate.py → _plate.py} +451 -78
  6. ngio/images/__init__.py +3 -3
  7. ngio/images/{image.py → _image.py} +26 -21
  8. ngio/images/{label.py → _label.py} +6 -4
  9. ngio/images/{masked_image.py → _masked_image.py} +2 -2
  10. ngio/images/{ome_zarr_container.py → _ome_zarr_container.py} +152 -86
  11. ngio/ome_zarr_meta/_meta_handlers.py +16 -8
  12. ngio/ome_zarr_meta/ngio_specs/_channels.py +41 -29
  13. ngio/tables/__init__.py +14 -2
  14. ngio/tables/_abstract_table.py +269 -0
  15. ngio/tables/{tables_container.py → _tables_container.py} +186 -100
  16. ngio/tables/backends/__init__.py +20 -0
  17. ngio/tables/backends/_abstract_backend.py +58 -80
  18. ngio/tables/backends/{_anndata_v1.py → _anndata.py} +5 -1
  19. ngio/tables/backends/_csv.py +35 -0
  20. ngio/tables/backends/{_json_v1.py → _json.py} +4 -1
  21. ngio/tables/backends/{_csv_v1.py → _non_zarr_backends.py} +61 -27
  22. ngio/tables/backends/_parquet.py +47 -0
  23. ngio/tables/backends/_table_backends.py +39 -18
  24. ngio/tables/backends/_utils.py +147 -1
  25. ngio/tables/v1/__init__.py +19 -3
  26. ngio/tables/v1/_condition_table.py +71 -0
  27. ngio/tables/v1/_feature_table.py +63 -129
  28. ngio/tables/v1/_generic_table.py +21 -159
  29. ngio/tables/v1/_roi_table.py +285 -201
  30. ngio/utils/_fractal_fsspec_store.py +29 -0
  31. {ngio-0.2.9.dist-info → ngio-0.3.0.dist-info}/METADATA +4 -3
  32. ngio-0.3.0.dist-info/RECORD +61 -0
  33. ngio/tables/_validators.py +0 -108
  34. ngio-0.2.9.dist-info/RECORD +0 -57
  35. /ngio/images/{abstract_image.py → _abstract_image.py} +0 -0
  36. /ngio/images/{create.py → _create.py} +0 -0
  37. {ngio-0.2.9.dist-info → ngio-0.3.0.dist-info}/WHEEL +0 -0
  38. {ngio-0.2.9.dist-info → ngio-0.3.0.dist-info}/licenses/LICENSE +0 -0
ngio/tables/__init__.py CHANGED
@@ -1,20 +1,28 @@
1
1
  """Ngio Tables implementations."""
2
2
 
3
- from ngio.tables.backends import ImplementedTableBackends
4
- from ngio.tables.tables_container import (
3
+ from ngio.tables._tables_container import (
4
+ ConditionTable,
5
5
  FeatureTable,
6
6
  GenericRoiTable,
7
7
  MaskingRoiTable,
8
8
  RoiTable,
9
9
  Table,
10
10
  TablesContainer,
11
+ TableType,
11
12
  TypedTable,
12
13
  open_table,
14
+ open_table_as,
13
15
  open_tables_container,
14
16
  )
17
+ from ngio.tables.backends import (
18
+ ImplementedTableBackends,
19
+ TableBackend,
20
+ TableBackendProtocol,
21
+ )
15
22
  from ngio.tables.v1._generic_table import GenericTable
16
23
 
17
24
  __all__ = [
25
+ "ConditionTable",
18
26
  "FeatureTable",
19
27
  "GenericRoiTable",
20
28
  "GenericTable",
@@ -22,8 +30,12 @@ __all__ = [
22
30
  "MaskingRoiTable",
23
31
  "RoiTable",
24
32
  "Table",
33
+ "TableBackend",
34
+ "TableBackendProtocol",
35
+ "TableType",
25
36
  "TablesContainer",
26
37
  "TypedTable",
27
38
  "open_table",
39
+ "open_table_as",
28
40
  "open_tables_container",
29
41
  ]
@@ -0,0 +1,269 @@
1
+ """Implementation of a generic table class."""
2
+
3
+ import builtins
4
+ from abc import ABC, abstractmethod
5
+ from typing import Literal, Self
6
+
7
+ import pandas as pd
8
+ import polars as pl
9
+ from anndata import AnnData
10
+
11
+ from ngio.tables.backends import (
12
+ BackendMeta,
13
+ ImplementedTableBackends,
14
+ TableBackend,
15
+ TableBackendProtocol,
16
+ TabularData,
17
+ convert_to_anndata,
18
+ convert_to_pandas,
19
+ convert_to_polars,
20
+ normalize_table,
21
+ )
22
+ from ngio.utils import NgioValueError, ZarrGroupHandler
23
+
24
+
25
+ class AbstractBaseTable(ABC):
26
+ """Abstract base class for a table.
27
+
28
+ This is used to define common methods and properties
29
+ for all tables.
30
+
31
+ This class is not meant to be used directly.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ table_data: TabularData | None = None,
37
+ *,
38
+ meta: BackendMeta | None = None,
39
+ ) -> None:
40
+ """Initialize the table."""
41
+ if meta is None:
42
+ meta = BackendMeta()
43
+
44
+ self._meta = meta
45
+ if table_data is not None:
46
+ table_data = normalize_table(
47
+ table_data,
48
+ index_key=meta.index_key,
49
+ index_type=meta.index_type,
50
+ )
51
+ self._table_data = table_data
52
+ self._table_backend = None
53
+
54
+ def __repr__(self) -> str:
55
+ """Return a string representation of the table."""
56
+ return f"{self.__class__.__name__}"
57
+
58
+ @staticmethod
59
+ @abstractmethod
60
+ def table_type() -> str:
61
+ """Return the type of the table."""
62
+ ...
63
+
64
+ @staticmethod
65
+ @abstractmethod
66
+ def version() -> str:
67
+ """The generic table does not have a version.
68
+
69
+ Since does not follow a specific schema.
70
+ """
71
+ ...
72
+
73
+ @property
74
+ def backend_name(self) -> str | None:
75
+ """Return the name of the backend."""
76
+ if self._table_backend is None:
77
+ return None
78
+ return self._table_backend.backend_name()
79
+
80
+ @property
81
+ def meta(self) -> BackendMeta:
82
+ """Return the metadata of the table."""
83
+ return self._meta
84
+
85
+ @property
86
+ def index_key(self) -> str | None:
87
+ """Get the index key."""
88
+ return self._meta.index_key
89
+
90
+ @property
91
+ def index_type(self) -> Literal["int", "str"] | None:
92
+ """Get the index type."""
93
+ return self._meta.index_type
94
+
95
+ def load_as_anndata(self) -> AnnData:
96
+ """Load the table as an AnnData object."""
97
+ if self._table_backend is None:
98
+ raise NgioValueError("No backend set for the table.")
99
+ return self._table_backend.load_as_anndata()
100
+
101
+ def load_as_pandas_df(self) -> pd.DataFrame:
102
+ """Load the table as a pandas DataFrame."""
103
+ if self._table_backend is None:
104
+ raise NgioValueError("No backend set for the table.")
105
+ return self._table_backend.load_as_pandas_df()
106
+
107
+ def load_as_polars_lf(self) -> pl.LazyFrame:
108
+ """Load the table as a polars LazyFrame."""
109
+ if self._table_backend is None:
110
+ raise NgioValueError("No backend set for the table.")
111
+ return self._table_backend.load_as_polars_lf()
112
+
113
+ @property
114
+ def table_data(self) -> TabularData:
115
+ """Return the table."""
116
+ if self._table_data is not None:
117
+ return self._table_data
118
+
119
+ if self._table_backend is None:
120
+ raise NgioValueError(
121
+ "The table does not have a DataFrame in memory nor a backend."
122
+ )
123
+
124
+ self._table_data = self._table_backend.load()
125
+ return self._table_data
126
+
127
+ @property
128
+ def dataframe(self) -> pd.DataFrame:
129
+ """Return the table as a DataFrame."""
130
+ return convert_to_pandas(
131
+ self.table_data, index_key=self.index_key, index_type=self.index_type
132
+ )
133
+
134
+ @property
135
+ def lazy_frame(self) -> pl.LazyFrame:
136
+ """Return the table as a LazyFrame."""
137
+ return convert_to_polars(
138
+ self.table_data, index_key=self.index_key, index_type=self.index_type
139
+ )
140
+
141
+ @property
142
+ def anndata(self) -> AnnData:
143
+ """Return the table as an AnnData object."""
144
+ return convert_to_anndata(self.table_data, index_key=self.index_key)
145
+
146
+ @staticmethod
147
+ def _load_backend(
148
+ meta: BackendMeta,
149
+ handler: ZarrGroupHandler,
150
+ backend: TableBackend,
151
+ ) -> TableBackendProtocol:
152
+ """Create a new ROI table from a Zarr group handler."""
153
+ if isinstance(backend, str):
154
+ return ImplementedTableBackends().get_backend(
155
+ backend_name=backend,
156
+ group_handler=handler,
157
+ index_key=meta.index_key,
158
+ index_type=meta.index_type,
159
+ )
160
+ backend.set_group_handler(
161
+ group_handler=handler,
162
+ index_key=meta.index_key,
163
+ index_type=meta.index_type,
164
+ )
165
+ return backend
166
+
167
+ def set_table_data(
168
+ self,
169
+ table_data: TabularData | None = None,
170
+ refresh: bool = False,
171
+ ) -> None:
172
+ """Set the table.
173
+
174
+ If an object is passed, it will be used as the table.
175
+ If None is passed, the table will be loaded from the backend.
176
+
177
+ If refresh is True, the table will be reloaded from the backend.
178
+ If table is not None, this will be ignored.
179
+ """
180
+ if table_data is not None:
181
+ if not isinstance(table_data, TabularData):
182
+ raise NgioValueError(
183
+ "The table must be a pandas DataFrame, polars LazyFrame, "
184
+ " or AnnData object."
185
+ )
186
+
187
+ self._table_data = normalize_table(
188
+ table_data,
189
+ index_key=self.index_key,
190
+ index_type=self.index_type,
191
+ )
192
+ return None
193
+
194
+ if self._table_data is not None and not refresh:
195
+ return None
196
+
197
+ if self._table_backend is None:
198
+ raise NgioValueError(
199
+ "The table does not have a DataFrame in memory nor a backend."
200
+ )
201
+ self._table_data = self._table_backend.load()
202
+
203
+ def set_backend(
204
+ self,
205
+ handler: ZarrGroupHandler | None = None,
206
+ backend: TableBackend = "anndata",
207
+ ) -> None:
208
+ """Set the backend of the table."""
209
+ if handler is None:
210
+ if self._table_backend is None:
211
+ raise NgioValueError(
212
+ "No backend set for the table yet. "
213
+ "A ZarrGroupHandler must be provided."
214
+ )
215
+ handler = self._table_backend.group_handler
216
+
217
+ meta = self._meta
218
+ _backend = self._load_backend(
219
+ meta=meta,
220
+ handler=handler,
221
+ backend=backend,
222
+ )
223
+ self._table_backend = _backend
224
+
225
+ @classmethod
226
+ def _from_handler(
227
+ cls,
228
+ handler: ZarrGroupHandler,
229
+ meta_model: builtins.type[BackendMeta],
230
+ backend: TableBackend | None = None,
231
+ ) -> Self:
232
+ """Create a new ROI table from a Zarr group handler."""
233
+ meta = meta_model(**handler.load_attrs())
234
+ table = cls(meta=meta)
235
+ if backend is None:
236
+ backend = meta.backend
237
+ table.set_backend(handler=handler, backend=backend)
238
+ return table
239
+
240
+ @classmethod
241
+ @abstractmethod
242
+ def from_handler(
243
+ cls,
244
+ handler: ZarrGroupHandler,
245
+ backend: TableBackend | None = None,
246
+ ) -> Self:
247
+ """Create a new ROI table from a Zarr group handler."""
248
+ pass
249
+
250
+ @classmethod
251
+ def from_table_data(cls, table_data: TabularData, meta: BackendMeta) -> Self:
252
+ """Create a new ROI table from a Zarr group handler."""
253
+ return cls(
254
+ table_data=table_data,
255
+ meta=meta,
256
+ )
257
+
258
+ def consolidate(self) -> None:
259
+ """Write the current state of the table to the Zarr file."""
260
+ if self._table_backend is None:
261
+ raise NgioValueError(
262
+ "No backend set for the table. "
263
+ "Please add the table to a OME-Zarr Image before calling consolidate."
264
+ )
265
+
266
+ self._table_backend.write(
267
+ self.table_data,
268
+ metadata=self._meta.model_dump(exclude_none=True),
269
+ )