Skip to content

Tables API reference

For the on-disk format of each table type, see the Table Specifications.

Opening tables

ngio.tables.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,
    )
    meta = _get_meta(handler)
    return ImplementedTables().get_table(
        meta=meta, handler=handler, backend=backend, strict=False
    )

ngio.tables.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,
    )

ngio.tables.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)

Tables container

ngio.tables.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

    # 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}"
        )

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

    filtered_tables = []
    for table_name in tables:
        tb_handler = self._get_table_group_handler(table_name)
        table_type = _get_meta(tb_handler).type
        if table_type == filter_types:
            filtered_tables.append(table_name)
    return filtered_tables

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)

    meta = _get_meta(table_handler)
    return ImplementedTables().get_table(
        meta=meta,
        handler=table_handler,
        backend=backend,
        strict=strict,
    )

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._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()
    if name not in existing_tables:
        existing_tables.append(name)
        self._group_handler.write_attrs({"tables": existing_tables})

Table types

ROI tables

ngio.tables.RoiTable module-attribute

RoiTable = RoiTableV1

ngio.tables.MaskingRoiTable module-attribute

MaskingRoiTable = MaskingRoiTableV1

ngio.tables.GenericRoiTable module-attribute

GenericRoiTable = GenericRoiTableV1

Feature tables

ngio.tables.FeatureTable module-attribute

FeatureTable = FeatureTableV1

Condition tables

ngio.tables.ConditionTable module-attribute

ConditionTable = ConditionTableV1

Generic tables

ngio.tables.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,
) -> GenericTable
Source code in src/ngio/tables/v1/_generic_table.py
@classmethod
def from_handler(
    cls,
    handler: ZarrGroupHandler,
    backend: TableBackend | None = None,
) -> "GenericTable":
    return cls._from_handler(
        handler=handler,
        backend=backend,
        meta_model=BackendMeta,
    )

Backends

ngio.tables.TableBackend module-attribute

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

ngio.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
            )