Skip to content

3. Tables

Tables are not part of the core OME-Zarr specification but can be used in ngio to store measurements, features, regions of interest (ROIs), and other tabular data. Ngio follows the Fractal's Table Spec.

Getting a table

We can list all available tables and load a specific table:

>>> ome_zarr_container.list_tables()
['FOV_ROI_table', 'nuclei_ROI_table', 'well_ROI_table', 'regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']

Ngio supports three types of tables: roi_table, feature_table, and masking_roi_table, as well as untyped generic_table.

ROI tables can be used to store arbitrary regions of interest (ROIs) in the image. Here for example we will load the FOV_ROI_table that contains the microscope field of view (FOV) ROIs:

>>> roi_table = ome_zarr_container.get_table("FOV_ROI_table") # Get a ROI table
>>> roi_table.get("FOV_1")
name='FOV_1' x_length=416.0 y_length=351.0 z_length=1.0 x=0.0 y=0.0 z=0.0 x_micrometer_original=np.float32(-1448.3) y_micrometer_original=np.float32(-1517.7)
2025-04-04T15:12:04.921458 image/svg+xml Matplotlib v3.10.1, https://matplotlib.org/ This will return all the ROIs in the table. ROIs can be used to slice the image data:
>>> roi = roi_table.get("FOV_1")
>>> roi_data = image.get_roi(roi)
>>> roi_data.shape
(3, 1, 540, 640)
This will return the image data for the specified ROI. 2025-04-04T15:12:05.088381 image/svg+xml Matplotlib v3.10.1, https://matplotlib.org/

Masking ROIs are a special type of ROIs that can be used to store ROIs for masked objects in the image. The nuclei_ROI_table contains the masks for the nuclei label in the image, and is indexed by the label id.

>>> masking_table = ome_zarr_container.get_table("nuclei_ROI_table") # Get a mask table
>>> masking_table.get(1)
name='100' x_length=11.375 y_length=14.625 z_length=1.0 x=33.63750076293945 y=18.850000381469727 z=0.0
ROIs can be used to slice the image data:
>>> roi = masking_table.get(100)
>>> roi_data = image.get_roi(roi)
>>> roi_data.shape
(3, 1, 22, 18)
This will return the image data for the specified ROI. 2025-04-04T15:12:05.627728 image/svg+xml Matplotlib v3.10.1, https://matplotlib.org/ See 4. Masked Images and Labels for more details on how to use the masking ROIs to load masked data.

Features tables are used to store measurements and are indexed by the label id

>>> feature_table = ome_zarr_container.get_table("regionprops_DAPI") # Get a feature table
>>> feature_table.dataframe.head(5) # only show the first 5 rows
label area bbox_area equivalent_diameter max_intensity mean_intensity min_intensity standard_deviation_intensity
1 2120 2655 15.9384 476 278.636 86 54.3438
2 327 456 8.54771 604 324.162 118 90.8471
3 1381 1749 13.8165 386 212.682 60 50.1696
4 2566 3588 16.9858 497 251.731 61 53.3072
5 4201 5472 20.0194 466 223.863 51 56.719

Creating a table

Tables (differently from Images and Labels) can be purely in memory objects, and don't need to be saved on disk.

>>> from ngio.tables import RoiTable
>>> from ngio import Roi
>>> roi = Roi(x=0, y=0, x_length=128, y_length=128, name="FOV_1")
>>> roi_table = RoiTable(rois=[roi])
RoiTableV1(num_rois=1)
If you would like to create on-the-fly a ROI table for the whole image:
>>> roi_table = ome_zarr_container.build_image_roi_table("whole_image")
>>> roi_table
RoiTableV1(num_rois=1)
The build_image_roi_table method will create a ROI table with a single ROI that covers the whole image. This table is not associated with the image and is purely in memory. If we want to save it to disk, we can use the add_table method:
>>> ome_zarr_container.add_table("new_roi_table", roi_table, overwrite=True)
>>> roi_table = ome_zarr_container.get_table("new_roi_table")
RoiTableV1(num_rois=1)

Similarly to the ROI table, we can create a masking ROI table on-the-fly: Let's for example create a masking ROI table for the nuclei label:

>>> masking_table = ome_zarr_container.build_masking_roi_table("nuclei")
>>> masking_table
MaskingRoiTableV1(num_rois=3006, reference_label=nuclei)

Feature tables can be created from a pandas Dataframe:

>>> from ngio.tables import FeatureTable
>>> import pandas as pd
>>> example_data = pd.DataFrame({"label": [1, 2, 3], "area": [100, 200, 300]})
>>> feature_table = FeatureTable(dataframe=example_data)
>>> feature_table
FeatureTableV1(num_rows=3, num_columns=1)

Sometimes you might want to create a table that doesn't fit into the ROI, Masking ROI, or Feature categories. In this case, you can use the GenericTable class, which allows you to store any tabular data. It can be created from a pandas Dataframe:

>>> from ngio.tables import GenericTable
>>> import pandas as pd
>>> example_data = pd.DataFrame({"area": [100, 200, 300], "perimeter": [50, 60, 70]})
>>> generic_table = GenericTable(dataframe=example_data)
>>> generic_table
GenericTable(num_rows=3, num_columns=2, mode=dataframe)
Or from an "AnnData" object:
>>> from ngio.tables import GenericTable
>>> import anndata as ad
>>> adata = ad.AnnData(X=np.random.rand(10, 5), obs=pd.DataFrame({"cell_type": ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]}))
>>> generic_table = GenericTable(anndata=adata)
>>> generic_table
GenericTable(mode=anndata)
The GenericTable class allows you to store any tabular data, and is a flexible way to work with tables in ngio.