Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ I/O for the `spatialdata` project.
.. autosummary::
:toctree: generated
curio
cosmx
visium
xenium
Expand Down
2 changes: 2 additions & 0 deletions src/spatialdata_io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from importlib.metadata import version

from spatialdata_io.readers.cosmx import cosmx
from spatialdata_io.readers.curio import curio
from spatialdata_io.readers.mcmicro import mcmicro
from spatialdata_io.readers.steinbock import steinbock
from spatialdata_io.readers.visium import visium
from spatialdata_io.readers.xenium import xenium

__all__ = [
"curio",
"visium",
"xenium",
"cosmx",
Expand Down
15 changes: 15 additions & 0 deletions src/spatialdata_io/_constants/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
from spatialdata_io._constants._enum import ModeEnum


@unique
class CurioKeys(ModeEnum):
"""Keys for *Curio* formatted dataset."""

# files and directories
ANNDATA_FILE = ".h5ad"
CLUSTER_ASSIGNMENT = "cluster_assignment.txt"
METRICS_FILE = "Metrics.csv"
VAR_FEATURES_CLUSTERS = "variable_features_clusters.txt"
VAR_FEATURES_MORANSI = "variable_features_moransi.txt"
# metadata
CATEGORY = "Category"
TOP_CLUSTER_DEFINING_FEATURES = "Top_cluster_defining_features"


@unique
class CosmxKeys(ModeEnum):
"""Keys for *Nanostring Cosmx* formatted dataset."""
Expand Down
84 changes: 84 additions & 0 deletions src/spatialdata_io/readers/curio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import annotations

from pathlib import Path
from typing import Optional

import anndata as ad
import pandas as pd
from spatialdata import SpatialData
from spatialdata.models import TableModel

from spatialdata_io._constants._constants import CurioKeys
from spatialdata_io._docs import inject_docs

__all__ = ["curio"]


@inject_docs(vx=CurioKeys)
def curio(
path: str | Path,
dataset_id: Optional[str] = None,
) -> SpatialData:
"""
Read *Curio* formatted dataset.

This function reads the following files:

- ``<dataset_id>_`{vx.ANNDATA_FILE!r}```: Counts and metadata file.
- ``<dataset_id>_`{vx.CLUSTER_ASSIGNMENT!r}```: Cluster assignment file.
- ``<dataset_id>_`{vx.METRICS_FILE!r}```: Metrics file.
- ``<dataset_id>_`{vx.VAR_FEATURES_CLUSTERS!r}```: Variable features clusters file.
- ``<dataset_id>_`{vx.VAR_FEATURES_MORANSI!r}```: Variable features Moran's I file.

.. seealso::

- `CODEX output <https://help.codex.bio/codex/processor/technical-notes/expected-output>`_.

Parameters
----------
path
Path to the directory containing the data.
dataset_id
Dataset identifier.
imread_kwargs
Keyword arguments passed to :func:`dask_image.imread.imread`.
image_models_kwargs
Keyword arguments passed to :class:`spatialdata.models.Image2DModel`.

Returns
-------
:class:`spatialdata.SpatialData`
"""
path = Path(path)
path_files = [
CurioKeys.ANNDATA_FILE,
CurioKeys.CLUSTER_ASSIGNMENT,
CurioKeys.METRICS_FILE,
CurioKeys.VAR_FEATURES_CLUSTERS,
CurioKeys.VAR_FEATURES_MORANSI,
]

if dataset_id is not None:
file_names = [f"{dataset_id}_{file_name}" for file_name in path_files]
else:
file_names = []
for file_name in path_files:
file_names.extend(str(path.glob(file_name)))

adata = ad.read_h5ad(path / file_names[0])
cluster_assign = pd.read_csv(path / file_names[1], sep="\t", header=None)
metrics = pd.read_csv(path / file_names[2], sep=r"\,", header=0)
var_features_clusters = pd.read_csv(path / file_names[3], sep="\t", header=0)
var_features_moransi = pd.read_csv(path / file_names[4], sep="\t", header=0)

adata.obs = adata.obs.assign(cluster=cluster_assign[1].values)
categories = metrics[CurioKeys.CATEGORY].unique()
for cat in categories:
df = metrics.loc[metrics[CurioKeys.CATEGORY] == cat]
adata.uns[cat] = dict(zip(df.iloc[:, 0], df.iloc[:, 1]))
adata.uns[CurioKeys.TOP_CLUSTER_DEFINING_FEATURES] = var_features_clusters
adata.var.join(var_features_moransi, how="outer")

table = TableModel.parse(adata)

return SpatialData(table=table)