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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Version 0.0.9
- Added `to_anndata()` in main `SpatialExperiment` class (PR #50)

## Version 0.0.8
- Set the expected column names for image data slot (PR #46)

## Version 0.0.7
- Added `img_source` function in main SpatialExperiment class and child classes of VirtualSpatialExperiment (PR #36)
- Added `remove_img` function (PR #34)
Expand Down
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()