Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Scene class #220

Merged
merged 13 commits into from
Sep 26, 2024
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies = [
# Remove this once we can specify a recent pyarrow.
"pyarrow-hotfix",
"scipy",
"shapely",
"typing-extensions>=4.1", # For LiteralString (py3.11)
]
requires-python = "~=3.8"
Expand Down Expand Up @@ -62,5 +63,5 @@ python_version = 3.8

[[tool.mypy.overrides]]
# These dependencies do not currently have canonical type stubs.
module = ["anndata", "pyarrow", "pyarrow_hotfix", "scipy"]
module = ["anndata", "pyarrow", "pyarrow.compute", "pyarrow_hotfix", "scipy", "shapely"]
ignore_missing_imports = true
1 change: 1 addition & 0 deletions python-spec/requirements-py3.10.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pyarrow-hotfix==0.6
python-dateutil==2.9.0.post0
pytz==2024.1
scipy==1.13.1
shapely==2.0.4
six==1.16.0
typing_extensions==4.12.2
tzdata==2024.1
1 change: 1 addition & 0 deletions python-spec/requirements-py3.11.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pyarrow==16.1.0
pyarrow-hotfix==0.6
python-dateutil==2.9.0.post0
pytz==2024.1
shapely==2.0.4
scipy==1.13.1
six==1.16.0
typing_extensions==4.12.2
Expand Down
1 change: 1 addition & 0 deletions python-spec/requirements-py3.12.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ python-dateutil==2.9.0.post0
pytz==2024.1
scipy==1.13.1
setuptools==70.0.0
shapely==2.0.4
six==1.16.0
typing_extensions==4.12.2
tzdata==2024.1
Expand Down
1 change: 1 addition & 0 deletions python-spec/requirements-py3.8-lint.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pytz==2024.1
PyYAML==6.0.1
ruff==0.4.9
scipy==1.10.1
shapely==2.0.4
six==1.16.0
tomli==2.0.1
types-pytz==2024.1.0.20240417
Expand Down
1 change: 1 addition & 0 deletions python-spec/requirements-py3.8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pyarrow-hotfix==0.6
python-dateutil==2.9.0.post0
pytz==2024.1
scipy==1.10.1
shapely==2.0.4
six==1.16.0
typing_extensions==4.12.2
tzdata==2024.1
Expand Down
1 change: 1 addition & 0 deletions python-spec/requirements-py3.9.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pytz==2024.1
rsa==4.7.2
s3transfer==0.6.0
scipy==1.13.1
shapely==2.0.4
six==1.16.0
typing_extensions==4.12.2
tzdata==2024.1
13 changes: 13 additions & 0 deletions python-spec/src/somacore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
from .query import AxisColumnNames
from .query import AxisQuery
from .query import ExperimentAxisQuery
from .scene import Scene
from .spatialdata import GeometryDataFrame
from .spatialdata import ImageProperties
from .spatialdata import MultiscaleImage
from .spatialdata import PointCloud
from .spatialdata import SpatialRead
from .types import ContextBase

try:
Expand All @@ -59,8 +65,15 @@
"ReadIter",
"SparseNDArray",
"SparseRead",
"SpatialRead",
"Experiment",
"Measurement",
"Scene",
"ImageProperties",
"MultiscaleImage",
"SpatialDataFrame",
"GeometryDataFrame",
"PointCloud",
"BatchSize",
"IOfN",
"ResultOrder",
Expand Down
2 changes: 2 additions & 0 deletions python-spec/src/somacore/ephemeral/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
from .collections import Collection
from .collections import Experiment
from .collections import Measurement
from .collections import Scene

__all__ = (
"Collection",
"Experiment",
"Measurement",
"Scene",
)
89 changes: 88 additions & 1 deletion python-spec/src/somacore/ephemeral/collections.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
from typing import Any, Dict, Iterator, NoReturn, Optional, TypeVar
from typing import (
Any,
Dict,
Iterator,
NoReturn,
Optional,
Sequence,
TypeVar,
Union,
)

from typing_extensions import Literal, Self

from .. import base
from .. import collection
from .. import coordinates
from .. import data
from .. import experiment
from .. import measurement
from .. import options
from .. import scene
from .. import spatialdata

_Elem = TypeVar("_Elem", bound=base.SOMAObject)

Expand Down Expand Up @@ -120,6 +132,14 @@ class Collection( # type: ignore[misc] # __eq__ false positive
]
"""The loosest possible constraint of the abstract Measurement type."""

_BasicAbstractScene = scene.Scene[
spatialdata.MultiscaleImage,
spatialdata.PointCloud,
spatialdata.GeometryDataFrame,
base.SOMAObject,
]
"""The loosest possible constraint of the abstract Scene type."""


class Measurement( # type: ignore[misc] # __eq__ false positive
BaseCollection[base.SOMAObject], _BasicAbstractMeasurement
Expand All @@ -129,11 +149,78 @@ class Measurement( # type: ignore[misc] # __eq__ false positive
__slots__ = ()


class Scene( # type: ignore[misc] # __eq__ false positive
BaseCollection[base.SOMAObject], _BasicAbstractScene
):
"""An in-memory Collection with Scene semantics."""

__slots__ = ()

@property
def coordinate_space(self) -> coordinates.CoordinateSpace:
"""Coordinate system for this scene."""
raise NotImplementedError()

@coordinate_space.setter
def coordinate_space(self, value: coordinates.CoordinateSpace) -> None:
raise NotImplementedError()

def register_geometry_dataframe(
self,
key: str,
transform: coordinates.CoordinateTransform,
*,
subcollection: Union[str, Sequence[str]] = "obsl",
coordinate_space: Optional[coordinates.CoordinateSpace] = None,
) -> spatialdata.GeometryDataFrame:
raise NotImplementedError()

def register_multiscale_image(
self,
key: str,
transform: coordinates.CoordinateTransform,
*,
subcollection: Union[str, Sequence[str]] = "img",
coordinate_space: Optional[coordinates.CoordinateSpace] = None,
) -> spatialdata.MultiscaleImage:
raise NotImplementedError()

def register_point_cloud(
self,
key: str,
transform: coordinates.CoordinateTransform,
*,
subcollection: Union[str, Sequence[str]] = "obsl",
coordinate_space: Optional[coordinates.CoordinateSpace] = None,
) -> spatialdata.PointCloud:
raise NotImplementedError()

def get_transformation_to_geometry_dataframe(
self, key: str, *, subcollection: Union[str, Sequence[str]] = "obsl"
):
raise NotImplementedError()

def get_transformation_to_multiscale_image(
self,
key: str,
*,
subcollection: str = "img",
level: Optional[Union[str, int]] = None,
) -> coordinates.CoordinateTransform:
raise NotImplementedError()

def get_transformation_to_point_cloud(
self, key: str, *, subcollection: str = "obsl"
) -> coordinates.CoordinateTransform:
raise NotImplementedError()


class Experiment( # type: ignore[misc] # __eq__ false positive
BaseCollection[base.SOMAObject],
experiment.Experiment[
data.DataFrame,
collection.Collection[_BasicAbstractMeasurement],
collection.Collection[_BasicAbstractScene],
base.SOMAObject,
],
):
Expand Down
11 changes: 10 additions & 1 deletion python-spec/src/somacore/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@
from . import data
from . import measurement
from . import query
from . import scene

_DF = TypeVar("_DF", bound=data.DataFrame)
"""An implementation of a DataFrame."""
_MeasColl = TypeVar("_MeasColl", bound=collection.Collection[measurement.Measurement])
"""An implementation of a collection of Measurements."""
_SceneColl = TypeVar("_SceneColl", bound=collection.Collection[scene.Scene])
"""An implemenation of a collection of spatial data."""
_RootSO = TypeVar("_RootSO", bound=base.SOMAObject)
"""The root SOMA object type of the implementation."""


class Experiment(collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _RootSO]):
class Experiment(
collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _SceneColl, _RootSO]
):
"""A collection subtype representing an annotated 2D matrix of measurements.

In single cell biology, this can represent multiple modes of measurement
Expand All @@ -38,6 +43,7 @@ class Experiment(collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _Ro
# somacore.Experiment[
# ImplDataFrame, # _DF
# ImplMeasurement, # _MeasColl
# ImplScene, # _SceneColl
# ImplSOMAObject, # _RootSO
# ],
# ):
Expand All @@ -57,6 +63,9 @@ class Experiment(collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _Ro
ms = _mixin.item[_MeasColl]()
"""A collection of named measurements."""

spatial = _mixin.item[_SceneColl]() # TODO: Discuss the name of this element.
"""A collection of named spatial scenes."""

def axis_query(
self,
measurement_name: str,
Expand Down
8 changes: 8 additions & 0 deletions python-spec/src/somacore/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@
import numpy as np
import numpy.typing as npt
import pyarrow as pa
import shapely
from typing_extensions import Final, Literal

from . import types

SOMA_JOINID: Final = "soma_joinid"
"""Global constant for the SOMA join ID."""

SOMA_GEOMETRY: Final = "soma_geometry"
"""Global constant for SOMA spatial geometry type."""

OpenMode = Literal["r", "w"]
"""How to open a SOMA object: read or write."""

Expand Down Expand Up @@ -177,5 +181,9 @@ class ResultOrder(enum.Enum):
pa.ChunkedArray,
]
"""A single coordinate range for one dimension of a sparse ndarray."""

SparseNDCoords = Sequence[SparseNDCoord]
"""A sequence of coordinate ranges for reading sparse ndarrays."""

SpatialRegion = Union[Sequence[int], Sequence[float], shapely.GeometryType]
"""A spatial region used for reading spatial dataframes and multiscale images."""
Loading