Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ exclude =
# PDF = ReportLab; RXP
optional =
pandas
requests
anndata
delayedarray

# Add here test requirements (semicolon/line-separated)
testing =
Expand Down
1 change: 0 additions & 1 deletion src/spatialexperiment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
)

__all__ = [
"ProxySpatialFeatureExperiment",
"read_tenx_visium",
"SpatialExperiment",
"LoadedSpatialImage",
Expand Down
48 changes: 47 additions & 1 deletion src/spatialexperiment/spatialexperiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@
_validate_spatial_coords,
_validate_spatial_coords_names,
)
from .spatialimage import VirtualSpatialImage, construct_spatial_image_class
from .spatialimage import (
VirtualSpatialImage,
StoredSpatialImage,
RemoteSpatialImage,
LoadedSpatialImage,
construct_spatial_image_class,
)

__author__ = "keviny2"
__copyright__ = "keviny2"
Expand Down Expand Up @@ -1009,6 +1015,46 @@ def mirror_img(self, sample_id=None, image_id=None, axis=("h", "v")):
def to_spatial_experiment():
raise NotImplementedError()

################################
######>> AnnData interop <<#####
################################

def to_anndata(self, include_alternative_experiments: bool = False) -> "anndata.AnnData":
"""Transform :py:class:`~SpatialExperiment`-like into a :py:class:`~anndata.AnnData` representation.

Args:
include_alternative_experiments:
Whether to transform alternative experiments.

Returns:
An ``AnnData`` representation of the experiment.
"""
obj, alt_exps = super().to_anndata(include_alternative_experiments=include_alternative_experiments)

if "spatial" in obj.uns:
raise ValueError("'spatial' key already exists in the metadata. Rename to something else.")

obj.uns["spatial"] = {}
for _, row in self.img_data:
library_id = row["sample_id"] + "-" + row["image_id"]
obj.uns["spatial"][library_id] = {}

spi = row["data"]
if isinstance(spi, LoadedSpatialImage):
img = spi.image
elif isinstance(spi, (StoredSpatialImage, RemoteSpatialImage)):
img = spi.img_source()

obj.uns["spatial"][library_id]["images"] = {"hires": img} # default to `hires` for now

obj.uns["spatial"][library_id]["scalefactors"] = {
"tissue_hires_scalef": row["scale_factor"]
} # default to `tissue_hires_scalef` for now

obj.obsm["spatial"] = np.column_stack([self.spatial_coordinates[axis] for axis in self.spatial_coords_names])

return obj, alt_exps

################################
#######>> combine ops <<########
################################
Expand Down
4 changes: 2 additions & 2 deletions src/spatialexperiment/spatialimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ def __str__(self) -> str:
######>> img props <<#######
############################

def get_image(self) -> Image.Image:
"""Get the image as a PIL Image object."""
def get_image(self) -> Union[Image.Image, np.ndarray]:
"""Get the image as a PIL Image object or ndarray."""

return self._image

Expand Down
37 changes: 37 additions & 0 deletions tests/test_to_anndata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from copy import deepcopy
from pathlib import Path
import pytest

__author__ = "keviny2"
__copyright__ = "keviny2"
__license__ = "MIT"


def test_to_anndata(spe):
obj, alt_exps = spe.to_anndata()

assert obj.shape == (500, 200)

# check that uns has the correct components
assert 'spatial' in obj.uns
assert len(obj.uns['spatial']) == 3

library_id = list(obj.uns['spatial'])[0]
assert 'images' in obj.uns['spatial'][library_id]
assert 'scalefactors' in obj.uns['spatial'][library_id]

assert 'hires' in obj.uns['spatial'][library_id]['images']
assert isinstance(obj.uns['spatial'][library_id]['images']['hires'], Path)

# check that obsm has the correct components
assert 'spatial' in obj.obsm
assert obj.obsm['spatial'].shape == (500, 2)


def test_to_anndata_spatial_key_exists(spe):
tspe = deepcopy(spe)

tspe.metadata['spatial'] = "123"

with pytest.raises(ValueError):
tspe.to_anndata()