Skip to content

ngio.tables API reference

ngio.tables

Ngio Tables implementations.

ROI_TABLE_TYPES module-attribute

ROI_TABLE_TYPES: tuple[str, ...] = (
    "roi_table",
    "masking_roi_table",
    "generic_roi_table",
)

ConditionTable module-attribute

ConditionTable = ConditionTableV1

FeatureTable module-attribute

FeatureTable = FeatureTableV1

GenericRoiTable module-attribute

GenericRoiTable = GenericRoiTableV1

MaskingRoiTable module-attribute

MaskingRoiTable = MaskingRoiTableV1

RoiTable module-attribute

RoiTable = RoiTableV1

TableType module-attribute

TableType = TypeVar('TableType', bound=Table)

TypedTable module-attribute

TypedTable = Literal[
    "generic_table",
    "roi_table",
    "masking_roi_table",
    "generic_roi_table",
    "feature_table",
    "condition_table",
]

DefaultTableBackend module-attribute

DefaultTableBackend = 'anndata_v1'

TableBackend module-attribute

TableBackend = (
    Literal["anndata", "json", "csv", "parquet"]
    | str
    | TableBackendProtocol
)

AbstractBaseTable

AbstractBaseTable(
    table_data: TabularData | None = None,
    *,
    meta: BackendMeta | None = None,
)

Bases: ABC

Abstract base class for a table.

This is used to define common methods and properties for all tables.

This class is not meant to be used directly.

Initialize the table.

Source code in src/ngio/tables/_abstract_table.py
def __init__(
    self,
    table_data: TabularData | None = None,
    *,
    meta: BackendMeta | None = None,
) -> None:
    """Initialize the table."""
    if meta is None:
        meta = BackendMeta()

    self._meta = meta
    if table_data is not None:
        table_data = normalize_table(
            table_data,
            index_key=meta.index_key,
            index_type=meta.index_type,
        )
    self._table_data = table_data
    self._table_backend = None
backend_name property
backend_name: str

Return the name of the backend.

If no backend is attached yet, the backend name stored in the table metadata is returned.

meta property
meta: BackendMeta

Return the metadata of the table.

index_key property
index_key: str | None

Get the index key.

index_type property
index_type: Literal['int', 'str'] | None

Get the index type.

table_data property
table_data: TabularData

Return the table.

dataframe property
dataframe: DataFrame

Return the table as a DataFrame.

lazy_frame property
lazy_frame: LazyFrame

Return the table as a LazyFrame.

anndata property
anndata: AnnData

Return the table as an AnnData object.

table_type abstractmethod staticmethod
table_type() -> str

Return the type of the table.

Source code in src/ngio/tables/_abstract_table.py
@staticmethod
@abstractmethod
def table_type() -> str:
    """Return the type of the table."""
    ...
version abstractmethod staticmethod
version() -> str

The generic table does not have a version.

Since does not follow a specific schema.

Source code in src/ngio/tables/_abstract_table.py
@staticmethod
@abstractmethod
def version() -> str:
    """The generic table does not have a version.

    Since does not follow a specific schema.
    """
    ...
load_as_anndata
load_as_anndata() -> AnnData

Load the table as an AnnData object.

Source code in src/ngio/tables/_abstract_table.py
def load_as_anndata(self) -> AnnData:
    """Load the table as an AnnData object."""
    if self._table_backend is None:
        raise NgioValueError("No backend set for the table.")
    return self._table_backend.load_as_anndata()
load_as_pandas_df
load_as_pandas_df() -> DataFrame

Load the table as a pandas DataFrame.

Source code in src/ngio/tables/_abstract_table.py
def load_as_pandas_df(self) -> pd.DataFrame:
    """Load the table as a pandas DataFrame."""
    if self._table_backend is None:
        raise NgioValueError("No backend set for the table.")
    return self._table_backend.load_as_pandas_df()
load_as_polars_lf
load_as_polars_lf() -> LazyFrame

Load the table as a polars LazyFrame.

Source code in src/ngio/tables/_abstract_table.py
def load_as_polars_lf(self) -> pl.LazyFrame:
    """Load the table as a polars LazyFrame."""
    if self._table_backend is None:
        raise NgioValueError("No backend set for the table.")
    return self._table_backend.load_as_polars_lf()
set_table_data
set_table_data(
    table_data: TabularData | None = None,
    refresh: bool = False,
) -> None

Set the table.

If an object is passed, it will be used as the table. If None is passed, the table will be loaded from the backend.

If refresh is True, the table will be reloaded from the backend. If table is not None, this will be ignored.

Source code in src/ngio/tables/_abstract_table.py
def set_table_data(
    self,
    table_data: TabularData | None = None,
    refresh: bool = False,
) -> None:
    """Set the table.

    If an object is passed, it will be used as the table.
    If None is passed, the table will be loaded from the backend.

    If refresh is True, the table will be reloaded from the backend.
        If table is not None, this will be ignored.
    """
    if table_data is not None:
        if not isinstance(table_data, TabularData):
            raise NgioValueError(
                "The table must be a pandas DataFrame, polars LazyFrame, "
                " or AnnData object."
            )

        self._table_data = normalize_table(
            table_data,
            index_key=self.index_key,
            index_type=self.index_type,
        )
        return None

    if self._table_data is not None and not refresh:
        return None

    if self._table_backend is None:
        raise NgioValueError(
            "The table does not have a DataFrame in memory nor a backend."
        )
    self._table_data = self._table_backend.load()
set_backend
set_backend(
    handler: ZarrGroupHandler | None = None,
    backend: TableBackend | None = None,
) -> None

Set the backend of the table.

If backend is None, the backend stored in the table metadata is used.

If no handler is provided and the table is not yet attached to a Zarr group, a string backend is only recorded as the table's preferred backend; the backend is instantiated when the table is written (e.g. by add_table).

Source code in src/ngio/tables/_abstract_table.py
def set_backend(
    self,
    handler: ZarrGroupHandler | None = None,
    backend: TableBackend | None = None,
) -> None:
    """Set the backend of the table.

    If `backend` is `None`, the backend stored in the table metadata
    is used.

    If no handler is provided and the table is not yet attached to a
    Zarr group, a string `backend` is only recorded as the table's
    preferred backend; the backend is instantiated when the table is
    written (e.g. by `add_table`).
    """
    if handler is None:
        if self._table_backend is None:
            if backend is None:
                return None
            if isinstance(backend, str):
                backends = ImplementedTableBackends()
                self._meta.backend = backends.normalize_backend_name(backend)
                return None
            raise NgioValueError(
                "A ZarrGroupHandler must be provided to attach a "
                "backend instance to the table."
            )
        handler = self._table_backend.group_handler

    meta = self._meta
    _backend = self._load_backend(
        meta=meta,
        handler=handler,
        backend=backend,
    )
    self._table_backend = _backend
    self._meta.backend = _backend.backend_name()
from_handler abstractmethod classmethod
from_handler(
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    attrs: dict | None = None,
) -> Self

Create a new ROI table from a Zarr group handler.

Source code in src/ngio/tables/_abstract_table.py
@classmethod
@abstractmethod
def from_handler(
    cls,
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    attrs: dict | None = None,
) -> Self:
    """Create a new ROI table from a Zarr group handler."""
    # Not `pass`: `abstractmethod` only guards instantiation, so a call on
    # a subclass that forgot to override this would silently return `None`
    # — `get_as(name, cls)` handed back nothing, with no error anywhere.
    raise NotImplementedError(f"{cls.__name__} does not implement `from_handler`.")
from_table_data classmethod
from_table_data(
    table_data: TabularData, meta: BackendMeta
) -> Self

Create a new ROI table from a Zarr group handler.

Source code in src/ngio/tables/_abstract_table.py
@classmethod
def from_table_data(cls, table_data: TabularData, meta: BackendMeta) -> Self:
    """Create a new ROI table from a Zarr group handler."""
    return cls(
        table_data=table_data,
        meta=meta,
    )
consolidate
consolidate() -> None

Write the current state of the table to the Zarr file.

Source code in src/ngio/tables/_abstract_table.py
def consolidate(self) -> None:
    """Write the current state of the table to the Zarr file."""
    if self._table_backend is None:
        raise NgioValueError(
            "No backend set for the table. "
            "Please add the table to a OME-Zarr Image before calling consolidate."
        )

    self._table_backend.write(
        self.table_data,
        metadata=self._meta.model_dump(exclude_none=True),
    )

ImplementedTables

A singleton class to manage the available table handler plugins.

available_implementations
available_implementations() -> list[str]

Get the available table handler versions.

Source code in src/ngio/tables/_tables_container.py
def available_implementations(self) -> list[str]:
    """Get the available table handler versions."""
    return list(self._implemented_tables.keys())
get_table
get_table(
    meta: TableMeta,
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    strict: bool = True,
    attrs: dict | None = None,
) -> Table

Try to get a handler for the given store based on the metadata version.

attrs are the group attributes meta was decoded from, forwarded so the concrete table does not read the same document again.

Source code in src/ngio/tables/_tables_container.py
def get_table(
    self,
    meta: TableMeta,
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    strict: bool = True,
    attrs: dict | None = None,
) -> Table:
    """Try to get a handler for the given store based on the metadata version.

    `attrs` are the group attributes `meta` was decoded from, forwarded so
    the concrete table does not read the same document again.
    """
    if strict:
        default = None
    else:
        default = GenericTable

    table_cls = self._implemented_tables.get(meta.unique_name(), default)
    if table_cls is None:
        raise NgioValueError(
            f"Table handler for {meta.unique_name()} not implemented."
        )
    table = table_cls.from_handler(handler=handler, backend=backend, attrs=attrs)
    return table
add_implementation
add_implementation(
    handler: type[Table],
    overwrite: bool = False,
    aliases: list[str] | None = None,
) -> None

Register a new table handler.

Source code in src/ngio/tables/_tables_container.py
def add_implementation(
    self,
    handler: type[Table],
    overwrite: bool = False,
    aliases: list[str] | None = None,
) -> None:
    """Register a new table handler."""
    meta = TableMeta(
        type=handler.table_type(),
        table_version=handler.version(),
    )

    self._add_implementation(handler, meta.unique_name(), overwrite)

    if aliases is not None:
        for alias in aliases:
            self._add_implementation(handler, alias, overwrite)

Table

Bases: Protocol

Placeholder class for a table.

backend_name property
backend_name: str

The name of the backend.

meta property
meta: BackendMeta

Return the metadata for the table.

dataframe property
dataframe: DataFrame

Return the table as a DataFrame.

lazy_frame property
lazy_frame: LazyFrame

Return the table as a LazyFrame.

anndata property
anndata: AnnData

Return the table as an AnnData object.

table_data property
table_data: TabularData

Return the table.

table_type staticmethod
table_type() -> str

Return the type of the table.

Source code in src/ngio/tables/_tables_container.py
@staticmethod
def table_type() -> str:
    """Return the type of the table."""
    ...
version staticmethod
version() -> str

Return the version of the table.

Source code in src/ngio/tables/_tables_container.py
@staticmethod
def version() -> str:
    """Return the version of the table."""
    ...
set_table_data
set_table_data(
    table_data: TabularData | None = None,
    refresh: bool = False,
) -> None

Make sure that the table data is set (exist in memory).

If an object is passed, it will be used as the table. If None is passed, the table will be loaded from the backend.

If refresh is True, the table will be reloaded from the backend. If table is not None, this will be ignored.

Source code in src/ngio/tables/_tables_container.py
def set_table_data(
    self,
    table_data: TabularData | None = None,
    refresh: bool = False,
) -> None:
    """Make sure that the table data is set (exist in memory).

    If an object is passed, it will be used as the table.
    If None is passed, the table will be loaded from the backend.

    If refresh is True, the table will be reloaded from the backend.
        If table is not None, this will be ignored.
    """
    ...
set_backend
set_backend(
    handler: ZarrGroupHandler | None = None,
    backend: TableBackend | None = None,
) -> None

Set the backend store and path for the table.

If the handler is None it will be inferred from the current backend. If the backend is None, the table's own backend is preserved.

Source code in src/ngio/tables/_tables_container.py
def set_backend(
    self,
    handler: ZarrGroupHandler | None = None,
    backend: TableBackend | None = None,
) -> None:
    """Set the backend store and path for the table.

    If the handler is `None` it will be inferred from the current backend.
    If the backend is `None`, the table's own backend is preserved.
    """
    ...
from_handler classmethod
from_handler(
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    attrs: dict | None = None,
) -> Self

Create a new table from a Zarr group handler.

Source code in src/ngio/tables/_tables_container.py
@classmethod
def from_handler(
    cls,
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    attrs: dict | None = None,
) -> Self:
    """Create a new table from a Zarr group handler."""
    ...
from_table_data classmethod
from_table_data(
    table_data: TabularData, meta: BackendMeta
) -> Self

Create a new table from a DataFrame.

Source code in src/ngio/tables/_tables_container.py
@classmethod
def from_table_data(cls, table_data: TabularData, meta: BackendMeta) -> Self:
    """Create a new table from a DataFrame."""
    ...
consolidate
consolidate() -> None

Consolidate the table on disk.

Source code in src/ngio/tables/_tables_container.py
def consolidate(self) -> None:
    """Consolidate the table on disk."""
    ...

TablesContainer

TablesContainer(group_handler: ZarrGroupHandler)

A class to handle the /tables group in an OME-NGFF file.

Initialize the TablesContainer.

Source code in src/ngio/tables/_tables_container.py
def __init__(self, group_handler: ZarrGroupHandler) -> None:
    """Initialize the TablesContainer."""
    self._group_handler = group_handler
    # name -> table type; see `table_types`.
    self._types_memo: dict[str, str] = {}

    # Validate the group
    # Either contains a tables attribute or is empty
    attrs = self._group_handler.load_attrs()
    if len(attrs) == 0:
        # It's an empty group
        pass
    elif "tables" in attrs and isinstance(attrs["tables"], list):
        # It's a valid group
        pass
    else:
        raise NgioValidationError(
            f"Invalid /tables group. "
            f"Expected a single tables attribute with a list of table names. "
            f"Found: {attrs}"
        )
table_types
table_types(
    names: Sequence[str] | None = None,
) -> dict[str, str]

Return the table type of each table, keyed by name.

The type lives only in each table's own attributes — the /tables group records names — so learning it costs one group open per table. Under cache=True that result is memoised: a table's type does not change without going through add/delete here, and those drop the memo. Under cache=False every call re-reads each type, so a table overwritten with a different type by another handle is picked up. Names are re-read every call in both modes.

Parameters:

  • names (Sequence[str] | None, default: None ) –

    Restrict to these tables. Defaults to every table listed.

Source code in src/ngio/tables/_tables_container.py
def table_types(self, names: Sequence[str] | None = None) -> dict[str, str]:
    """Return the table type of each table, keyed by name.

    The type lives only in each table's own attributes — the `/tables`
    group records names — so learning it costs one group open per table.
    Under `cache=True` that result is memoised: a table's type does not
    change without going through `add`/`delete` here, and those drop the
    memo. Under `cache=False` every call re-reads each type, so a table
    overwritten with a different type by another handle is picked up.
    Names are re-read every call in both modes.

    Args:
        names: Restrict to these tables. Defaults to every table listed.
    """
    if names is None:
        names = self._get_tables_list()

    use_memo = self._group_handler.use_cache
    for name in names:
        if not use_memo or name not in self._types_memo:
            try:
                handler = self._get_table_group_handler(name)
            except NgioFileNotFoundError:
                # A stale name in the `tables` attribute with no group
                # behind it. Not memoised (and any earlier memo is
                # dropped): it stays invisible to typed listings but
                # keeps raising on a direct `get`.
                self._types_memo.pop(name, None)
                continue
            self._types_memo[name] = _get_meta(handler).type
    return {
        name: self._types_memo[name] for name in names if name in self._types_memo
    }
list
list(
    filter_types: TypedTable | str | None = None,
) -> list[str]

List all tables in the group.

Parameters:

  • filter_types (TypedTable | str | None, default: None ) –

    If provided, only return tables of this type.

Returns:

  • list[str] –

    A list of table names.

Source code in src/ngio/tables/_tables_container.py
def list(self, filter_types: TypedTable | str | None = None) -> list[str]:
    """List all tables in the group.

    Args:
        filter_types: If provided, only return tables of this type.

    Returns:
        A list of table names.
    """
    tables = self._get_tables_list()
    if filter_types is None:
        return tables

    return [
        name
        for name, table_type in self.table_types(tables).items()
        if table_type == filter_types
    ]
get
get(
    name: str,
    backend: TableBackend | None = None,
    strict: bool = True,
) -> Table

Get a table from the group.

Parameters:

  • name (str) –

    The name of the table.

  • backend (TableBackend | None, default: None ) –

    The backend to use for reading the table.

  • strict (bool, default: True ) –

    If True, raise an error if the table type is not implemented.

Returns:

  • Table –

    The table object.

Source code in src/ngio/tables/_tables_container.py
def get(
    self,
    name: str,
    backend: TableBackend | None = None,
    strict: bool = True,
) -> Table:
    """Get a table from the group.

    Args:
        name: The name of the table.
        backend: The backend to use for reading the table.
        strict: If True, raise an error if the table type is not implemented.

    Returns:
        The table object.
    """
    if name not in self.list():
        raise NgioValueError(f"Table '{name}' not found in the group.")

    table_handler = self._get_table_group_handler(name)

    # Read once: `TableMeta` picks the class and the concrete model is
    # built by the class — both consume this same document.
    attrs = table_handler.load_attrs()
    meta = _get_meta(table_handler, attrs=attrs)
    return ImplementedTables().get_table(
        meta=meta,
        handler=table_handler,
        backend=backend,
        strict=strict,
        attrs=attrs,
    )
get_as
get_as(
    name: str,
    table_cls: type[TableType],
    backend: TableBackend | None = None,
) -> TableType

Get a table from the group as a specific type.

Parameters:

  • name (str) –

    The name of the table.

  • table_cls (type[TableType]) –

    The table class to use for loading the table.

  • backend (TableBackend | None, default: None ) –

    The backend to use for reading the table.

Returns:

  • TableType –

    The table object of the specified type.

Source code in src/ngio/tables/_tables_container.py
def get_as(
    self,
    name: str,
    table_cls: type[TableType],
    backend: TableBackend | None = None,
) -> TableType:
    """Get a table from the group as a specific type.

    Args:
        name: The name of the table.
        table_cls: The table class to use for loading the table.
        backend: The backend to use for reading the table.

    Returns:
        The table object of the specified type.
    """
    if name not in self.list():
        raise NgioValueError(f"Table '{name}' not found in the group.")

    table_handler = self._get_table_group_handler(name)
    return table_cls.from_handler(
        handler=table_handler,
        backend=backend,
    )
delete
delete(name: str, missing_ok: bool = False) -> None

Delete a table from the group.

Parameters:

  • name (str) –

    The name of the table to delete.

  • missing_ok (bool, default: False ) –

    If True, do not raise an error if the table does not exist.

Source code in src/ngio/tables/_tables_container.py
def delete(self, name: str, missing_ok: bool = False) -> None:
    """Delete a table from the group.

    Args:
        name (str): The name of the table to delete.
        missing_ok (bool): If True, do not raise an error if
            the table does not exist.
    """
    existing_tables = self._get_tables_list()
    if name not in existing_tables:
        if missing_ok:
            return
        raise NgioValueError(
            f"Table '{name}' not found in the Tables group. "
            f"Available tables: {existing_tables}"
        )

    self._group_handler.delete_group(name)
    existing_tables.remove(name)
    self._types_memo.pop(name, None)
    self._group_handler.write_attrs({"tables": existing_tables})
add
add(
    name: str,
    table: Table,
    backend: TableBackend | None = None,
    overwrite: bool = False,
) -> None

Add a table to the group.

Parameters:

  • name (str) –

    The name of the table.

  • table (Table) –

    The table object to add.

  • backend (TableBackend | None, default: None ) –

    The backend to use for writing the table. If None (default), the table's own backend is preserved.

  • overwrite (bool, default: False ) –

    Whether to overwrite an existing table with the same name.

Source code in src/ngio/tables/_tables_container.py
def add(
    self,
    name: str,
    table: Table,
    backend: TableBackend | None = None,
    overwrite: bool = False,
) -> None:
    """Add a table to the group.

    Args:
        name: The name of the table.
        table: The table object to add.
        backend: The backend to use for writing the table. If `None`
            (default), the table's own backend is preserved.
        overwrite: Whether to overwrite an existing table with the same name.
    """
    existing_tables = self._get_tables_list()
    if name in existing_tables and not overwrite:
        raise NgioValueError(
            f"Table '{name}' already exists in the group. "
            "Use overwrite=True to replace it."
        )

    table_handler = self._group_handler.get_handler(path=name, overwrite=overwrite)

    if backend is None:
        backend = table.backend_name

    table.set_table_data()
    table.set_backend(
        handler=table_handler,
        backend=backend,
    )
    table.consolidate()
    # The type is written by `consolidate`, and `overwrite=True` can change
    # it, so drop any memoised value rather than trusting it.
    self._types_memo.pop(name, None)
    if name not in existing_tables:
        existing_tables.append(name)
        self._group_handler.write_attrs({"tables": existing_tables})

ImplementedTableBackends

A class to manage the available table backends.

available_backends property
available_backends: list[str]

Return the available table backends.

normalize_backend_name
normalize_backend_name(backend_name: str) -> str

Resolve a backend name or alias to its canonical name.

Raises:

Source code in src/ngio/tables/backends/_table_backends.py
def normalize_backend_name(self, backend_name: str) -> str:
    """Resolve a backend name or alias to its canonical name.

    Raises:
        NgioValueError: If the backend name is not implemented.
    """
    if backend_name not in self._implemented_backends:
        raise NgioValueError(f"Table backend {backend_name} not implemented.")
    return self._implemented_backends[backend_name].backend_name()
get_backend
get_backend(
    *,
    group_handler: ZarrGroupHandler,
    backend_name: str,
    index_key: str | None = None,
    index_type: Literal["int", "str"] | None = None,
) -> TableBackendProtocol

Instantiate the named backend and attach it to group_handler.

Raises:

Source code in src/ngio/tables/backends/_table_backends.py
def get_backend(
    self,
    *,
    group_handler: ZarrGroupHandler,
    backend_name: str,
    index_key: str | None = None,
    index_type: Literal["int", "str"] | None = None,
) -> TableBackendProtocol:
    """Instantiate the named backend and attach it to `group_handler`.

    Raises:
        NgioValueError: If `backend_name` is not registered.
    """
    if backend_name not in self._implemented_backends:
        raise NgioValueError(f"Table backend {backend_name} not implemented.")
    backend = self._implemented_backends[backend_name]()
    backend.set_group_handler(
        group_handler=group_handler, index_key=index_key, index_type=index_type
    )
    return backend
add_backend
add_backend(
    table_backend: type[TableBackendProtocol],
    overwrite: bool = False,
    aliases: list[str] | None = None,
) -> None

Register a new handler.

Source code in src/ngio/tables/backends/_table_backends.py
def add_backend(
    self,
    table_backend: type[TableBackendProtocol],
    overwrite: bool = False,
    aliases: list[str] | None = None,
) -> None:
    """Register a new handler."""
    self._add_backend(
        table_backend=table_backend,
        name=table_backend.backend_name(),
        overwrite=overwrite,
    )
    if aliases is not None:
        for alias in aliases:
            self._add_backend(
                table_backend=table_backend, name=alias, overwrite=overwrite
            )

TableBackendProtocol

Bases: Protocol

group_handler property
group_handler: ZarrGroupHandler

Return the group handler.

set_group_handler
set_group_handler(
    group_handler: ZarrGroupHandler,
    index_key: str | None = None,
    index_type: Literal["int", "str"] | None = None,
) -> None

Attach a group handler to the backend.

Index keys and index types are used to ensure that the serialization and deserialization of the table is consistent across different backends.

Making sure that this is consistent is a duty of the backend implementations.

Source code in src/ngio/tables/backends/_table_backends.py
def set_group_handler(
    self,
    group_handler: ZarrGroupHandler,
    index_key: str | None = None,
    index_type: Literal["int", "str"] | None = None,
) -> None:
    """Attach a group handler to the backend.

    Index keys and index types are used to ensure that the
    serialization and deserialization of the table
    is consistent across different backends.

    Making sure that this is consistent is
    a duty of the backend implementations.
    """
    ...
backend_name staticmethod
backend_name() -> str

Return the name of the backend.

As a convention we set name as

{backend_name}_v{version}

Where the version is a integer.

Source code in src/ngio/tables/backends/_table_backends.py
@staticmethod
def backend_name() -> str:
    """Return the name of the backend.

    As a convention we set name as:
        {backend_name}_v{version}

    Where the version is a integer.
    """
    ...
implements_anndata staticmethod
implements_anndata() -> bool

Check if the backend implements the anndata protocol.

If this is True, the backend should implement the write_from_anndata method.

AnnData objects are more complex than DataFrames, so if this is true the backend should implement the full serialization of the AnnData object.

If this is False, these methods should raise a NotImplementedError.

Source code in src/ngio/tables/backends/_table_backends.py
@staticmethod
def implements_anndata() -> bool:
    """Check if the backend implements the anndata protocol.

    If this is True, the backend should implement the
    `write_from_anndata` method.

    AnnData objects are more complex than DataFrames,
    so if this is true the backend should implement the
    full serialization of the AnnData object.

    If this is False, these methods should raise a
    `NotImplementedError`.
    """
    ...
implements_pandas staticmethod
implements_pandas() -> bool

Check if the backend implements the pandas protocol.

If this is True, the backend should implement the write_from_dataframe methods.

If this is False, these methods should raise a NotImplementedError.

Source code in src/ngio/tables/backends/_table_backends.py
@staticmethod
def implements_pandas() -> bool:
    """Check if the backend implements the pandas protocol.

    If this is True, the backend should implement the
    `write_from_dataframe` methods.

    If this is False, these methods should raise a
    `NotImplementedError`.
    """
    ...
implements_polars staticmethod
implements_polars() -> bool

Check if the backend implements the polars protocol.

If this is True, the backend should implement the write_from_polars methods.

If this is False, these methods should raise a NotImplementedError.

Source code in src/ngio/tables/backends/_table_backends.py
@staticmethod
def implements_polars() -> bool:
    """Check if the backend implements the polars protocol.

    If this is True, the backend should implement the
    `write_from_polars` methods.

    If this is False, these methods should raise a
    `NotImplementedError`.
    """
    ...
load_as_anndata
load_as_anndata() -> AnnData

Load the table as an AnnData object.

Source code in src/ngio/tables/backends/_table_backends.py
def load_as_anndata(self) -> AnnData:
    """Load the table as an AnnData object."""
    ...
load_as_pandas_df
load_as_pandas_df() -> DataFrame

Load the table as a pandas DataFrame.

Source code in src/ngio/tables/backends/_table_backends.py
def load_as_pandas_df(self) -> DataFrame:
    """Load the table as a pandas DataFrame."""
    ...
load_as_polars_lf
load_as_polars_lf() -> LazyFrame

Load the table as a polars LazyFrame.

Source code in src/ngio/tables/backends/_table_backends.py
def load_as_polars_lf(self) -> LazyFrame:
    """Load the table as a polars LazyFrame."""
    ...
load
load() -> TabularData

The default load method.

This method will be default way to load the table from the backend. This method should wrap one of the load_as_anndata, load_as_dataframe or load_as_polars methods depending on the backend implementation.

Source code in src/ngio/tables/backends/_table_backends.py
def load(self) -> TabularData:
    """The default load method.

    This method will be default way to load the table
    from the backend. This method should wrap one of the
    `load_as_anndata`, `load_as_dataframe` or `load_as_polars`
    methods depending on the backend implementation.
    """
    ...
write_from_pandas
write_from_pandas(table: DataFrame) -> None

Serialize the table from a pandas DataFrame.

Source code in src/ngio/tables/backends/_table_backends.py
def write_from_pandas(self, table: DataFrame) -> None:
    """Serialize the table from a pandas DataFrame."""
    ...
write_from_anndata
write_from_anndata(table: AnnData) -> None

Serialize the table from an AnnData object.

Source code in src/ngio/tables/backends/_table_backends.py
def write_from_anndata(self, table: AnnData) -> None:
    """Serialize the table from an AnnData object."""
    ...
write_from_polars
write_from_polars(table: LazyFrame | DataFrame) -> None

Serialize the table from a polars DataFrame or LazyFrame.

Source code in src/ngio/tables/backends/_table_backends.py
def write_from_polars(self, table: LazyFrame | PolarsDataFrame) -> None:
    """Serialize the table from a polars DataFrame or LazyFrame."""
    ...
write
write(
    table_data: DataFrame | AnnData | DataFrame | LazyFrame,
    metadata: dict[str, str] | None = None,
) -> None

This is a generic write method.

Will call the appropriate write method depending on the type of the table.

Moreover it will also write the metadata if provided, and the backend methadata

the backend should write in the zarr group attributes - backend: the backend name (self.backend_name()) - index_key: the index key - index_type: the index type

Source code in src/ngio/tables/backends/_table_backends.py
def write(
    self,
    table_data: DataFrame | AnnData | PolarsDataFrame | LazyFrame,
    metadata: dict[str, str] | None = None,
) -> None:
    """This is a generic write method.

    Will call the appropriate write method
    depending on the type of the table.

    Moreover it will also write the metadata
    if provided, and the backend methadata

    the backend should write in the zarr group attributes
        - backend: the backend name (self.backend_name())
        - index_key: the index key
        - index_type: the index type

    """

GenericTable

GenericTable(
    table_data: TabularData | None = None,
    *,
    meta: BackendMeta | None = None,
)

Bases: AbstractBaseTable

Class to a non-specific table.

This can be used to load any table that does not have a specific definition.

Initialize the table.

Source code in src/ngio/tables/_abstract_table.py
def __init__(
    self,
    table_data: TabularData | None = None,
    *,
    meta: BackendMeta | None = None,
) -> None:
    """Initialize the table."""
    if meta is None:
        meta = BackendMeta()

    self._meta = meta
    if table_data is not None:
        table_data = normalize_table(
            table_data,
            index_key=meta.index_key,
            index_type=meta.index_type,
        )
    self._table_data = table_data
    self._table_backend = None
backend_name property
backend_name: str

Return the name of the backend.

If no backend is attached yet, the backend name stored in the table metadata is returned.

meta property
meta: BackendMeta

Return the metadata of the table.

index_key property
index_key: str | None

Get the index key.

index_type property
index_type: Literal['int', 'str'] | None

Get the index type.

table_data property
table_data: TabularData

Return the table.

dataframe property
dataframe: DataFrame

Return the table as a DataFrame.

lazy_frame property
lazy_frame: LazyFrame

Return the table as a LazyFrame.

anndata property
anndata: AnnData

Return the table as an AnnData object.

load_as_anndata
load_as_anndata() -> AnnData

Load the table as an AnnData object.

Source code in src/ngio/tables/_abstract_table.py
def load_as_anndata(self) -> AnnData:
    """Load the table as an AnnData object."""
    if self._table_backend is None:
        raise NgioValueError("No backend set for the table.")
    return self._table_backend.load_as_anndata()
load_as_pandas_df
load_as_pandas_df() -> DataFrame

Load the table as a pandas DataFrame.

Source code in src/ngio/tables/_abstract_table.py
def load_as_pandas_df(self) -> pd.DataFrame:
    """Load the table as a pandas DataFrame."""
    if self._table_backend is None:
        raise NgioValueError("No backend set for the table.")
    return self._table_backend.load_as_pandas_df()
load_as_polars_lf
load_as_polars_lf() -> LazyFrame

Load the table as a polars LazyFrame.

Source code in src/ngio/tables/_abstract_table.py
def load_as_polars_lf(self) -> pl.LazyFrame:
    """Load the table as a polars LazyFrame."""
    if self._table_backend is None:
        raise NgioValueError("No backend set for the table.")
    return self._table_backend.load_as_polars_lf()
set_table_data
set_table_data(
    table_data: TabularData | None = None,
    refresh: bool = False,
) -> None

Set the table.

If an object is passed, it will be used as the table. If None is passed, the table will be loaded from the backend.

If refresh is True, the table will be reloaded from the backend. If table is not None, this will be ignored.

Source code in src/ngio/tables/_abstract_table.py
def set_table_data(
    self,
    table_data: TabularData | None = None,
    refresh: bool = False,
) -> None:
    """Set the table.

    If an object is passed, it will be used as the table.
    If None is passed, the table will be loaded from the backend.

    If refresh is True, the table will be reloaded from the backend.
        If table is not None, this will be ignored.
    """
    if table_data is not None:
        if not isinstance(table_data, TabularData):
            raise NgioValueError(
                "The table must be a pandas DataFrame, polars LazyFrame, "
                " or AnnData object."
            )

        self._table_data = normalize_table(
            table_data,
            index_key=self.index_key,
            index_type=self.index_type,
        )
        return None

    if self._table_data is not None and not refresh:
        return None

    if self._table_backend is None:
        raise NgioValueError(
            "The table does not have a DataFrame in memory nor a backend."
        )
    self._table_data = self._table_backend.load()
set_backend
set_backend(
    handler: ZarrGroupHandler | None = None,
    backend: TableBackend | None = None,
) -> None

Set the backend of the table.

If backend is None, the backend stored in the table metadata is used.

If no handler is provided and the table is not yet attached to a Zarr group, a string backend is only recorded as the table's preferred backend; the backend is instantiated when the table is written (e.g. by add_table).

Source code in src/ngio/tables/_abstract_table.py
def set_backend(
    self,
    handler: ZarrGroupHandler | None = None,
    backend: TableBackend | None = None,
) -> None:
    """Set the backend of the table.

    If `backend` is `None`, the backend stored in the table metadata
    is used.

    If no handler is provided and the table is not yet attached to a
    Zarr group, a string `backend` is only recorded as the table's
    preferred backend; the backend is instantiated when the table is
    written (e.g. by `add_table`).
    """
    if handler is None:
        if self._table_backend is None:
            if backend is None:
                return None
            if isinstance(backend, str):
                backends = ImplementedTableBackends()
                self._meta.backend = backends.normalize_backend_name(backend)
                return None
            raise NgioValueError(
                "A ZarrGroupHandler must be provided to attach a "
                "backend instance to the table."
            )
        handler = self._table_backend.group_handler

    meta = self._meta
    _backend = self._load_backend(
        meta=meta,
        handler=handler,
        backend=backend,
    )
    self._table_backend = _backend
    self._meta.backend = _backend.backend_name()
from_table_data classmethod
from_table_data(
    table_data: TabularData, meta: BackendMeta
) -> Self

Create a new ROI table from a Zarr group handler.

Source code in src/ngio/tables/_abstract_table.py
@classmethod
def from_table_data(cls, table_data: TabularData, meta: BackendMeta) -> Self:
    """Create a new ROI table from a Zarr group handler."""
    return cls(
        table_data=table_data,
        meta=meta,
    )
consolidate
consolidate() -> None

Write the current state of the table to the Zarr file.

Source code in src/ngio/tables/_abstract_table.py
def consolidate(self) -> None:
    """Write the current state of the table to the Zarr file."""
    if self._table_backend is None:
        raise NgioValueError(
            "No backend set for the table. "
            "Please add the table to a OME-Zarr Image before calling consolidate."
        )

    self._table_backend.write(
        self.table_data,
        metadata=self._meta.model_dump(exclude_none=True),
    )
table_type staticmethod
table_type() -> str

Return the type of the table.

Source code in src/ngio/tables/v1/_generic_table.py
@staticmethod
def table_type() -> str:
    """Return the type of the table."""
    return "generic_table"
version staticmethod
version() -> str

The generic table does not have a version.

Since does not follow a specific schema.

Source code in src/ngio/tables/v1/_generic_table.py
@staticmethod
def version() -> str:
    """The generic table does not have a version.

    Since does not follow a specific schema.
    """
    return "1"
from_handler classmethod
from_handler(
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    attrs: dict | None = None,
) -> GenericTable
Source code in src/ngio/tables/v1/_generic_table.py
@classmethod
def from_handler(
    cls,
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
    attrs: dict | None = None,
) -> "GenericTable":
    return cls._from_handler(
        handler=handler,
        backend=backend,
        meta_model=BackendMeta,
        attrs=attrs,
    )

open_table

open_table(
    store: StoreOrGroup,
    backend: TableBackend | None = None,
    cache: bool = False,
    mode: AccessModeLiteral = "r+",
) -> Table

Open a table from a Zarr store.

Source code in src/ngio/tables/_tables_container.py
def open_table(
    store: StoreOrGroup,
    backend: TableBackend | None = None,
    cache: bool = False,
    mode: AccessModeLiteral = "r+",
) -> Table:
    """Open a table from a Zarr store."""
    handler = ZarrGroupHandler(
        store=store,
        cache=cache,
        mode=mode,
    )
    # Read once, decode once: the same document picks the class and builds it.
    attrs = handler.load_attrs()
    meta = _get_meta(handler, attrs=attrs)
    return ImplementedTables().get_table(
        meta=meta, handler=handler, backend=backend, strict=False, attrs=attrs
    )

open_table_as

open_table_as(
    store: StoreOrGroup,
    table_cls: type[TableType],
    backend: TableBackend | None = None,
    cache: bool = False,
    mode: AccessModeLiteral = "r+",
) -> TableType

Open a table from a Zarr store as a specific type.

Source code in src/ngio/tables/_tables_container.py
def open_table_as(
    store: StoreOrGroup,
    table_cls: type[TableType],
    backend: TableBackend | None = None,
    cache: bool = False,
    mode: AccessModeLiteral = "r+",
) -> TableType:
    """Open a table from a Zarr store as a specific type."""
    handler = ZarrGroupHandler(
        store=store,
        cache=cache,
        mode=mode,
    )
    return table_cls.from_handler(
        handler=handler,
        backend=backend,
    )

open_tables_container

open_tables_container(
    store: StoreOrGroup,
    cache: bool = False,
    mode: AccessModeLiteral = "r+",
) -> TablesContainer

Open a table handler from a Zarr store.

Source code in src/ngio/tables/_tables_container.py
def open_tables_container(
    store: StoreOrGroup,
    cache: bool = False,
    mode: AccessModeLiteral = "r+",
) -> TablesContainer:
    """Open a table handler from a Zarr store."""
    handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
    return TablesContainer(handler)

write_table

write_table(
    store: StoreOrGroup,
    table: Table,
    backend: TableBackend | None = None,
    cache: bool = False,
    mode: AccessModeLiteral = "a",
) -> None

Write a table to a Zarr store.

A table will be created at the given store location.

Parameters:

  • store (StoreOrGroup) –

    The Zarr store or group to write the table to.

  • table (Table) –

    The table to write.

  • backend (TableBackend, default: None ) –

    The backend to use for writing the table. If None (default), the table's own backend is preserved.

  • cache (bool, default: False ) –

    Whether to use caching for the Zarr group handler.

  • mode (AccessModeLiteral, default: 'a' ) –

    The access mode to use for the Zarr group handler.

Source code in src/ngio/tables/_tables_container.py
def write_table(
    store: StoreOrGroup,
    table: Table,
    backend: TableBackend | None = None,
    cache: bool = False,
    mode: AccessModeLiteral = "a",
) -> None:
    """Write a table to a Zarr store.

    A table will be created at the given store location.

    Args:
        store (StoreOrGroup): The Zarr store or group to write the table to.
        table (Table): The table to write.
        backend (TableBackend): The backend to use for writing the table.
            If `None` (default), the table's own backend is preserved.
        cache (bool): Whether to use caching for the Zarr group handler.
        mode (AccessModeLiteral): The access mode to use for the Zarr group handler.

    """
    handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
    # Materialize the data from the current backend (or in-memory) before swapping
    # to the new backend, mirroring TablesContainer.add. Without this, a table just
    # opened from another store (lazy, no in-memory data) would have its backend
    # replaced by the empty destination and consolidate would write nothing.
    table.set_table_data()
    table.set_backend(
        handler=handler,
        backend=backend,
    )
    table.consolidate()