diff --git a/python-spec/src/somacore/query/query.py b/python-spec/src/somacore/query/query.py index 2fad85ec..6fb9652f 100644 --- a/python-spec/src/somacore/query/query.py +++ b/python-spec/src/somacore/query/query.py @@ -25,6 +25,7 @@ from .. import data from .. import measurement from .. import options +from .. import scene from .. import types as base_types from . import _fast_csr from . import axis @@ -265,6 +266,45 @@ def varm(self, layer: str) -> data.SparseRead: """ return self._axism_inner(_Axis.VAR, layer) + def obsl(self, scene_id: str, layer: str) -> pa.Table: + """Returns an ``obsl`` layer from a scene as a sparse read. + + Lifecycle: experimental + """ + # Get the requested scene. + _scene = self._scene(scene_id) + try: + _obsl = _scene.obsl + except KeyError as ke: + raise ValueError( + f"Scene '{scene_id}' does not conatin contain obsl data" + ) from ke + + # Get the requested layer inside obsl. Check it is a geometry dataframe. + try: + _layer = _obsl[layer] + except KeyError as ke: + raise ValueError( + f"Layer '{layer}' is not available in Scene '{scene_id}' obsl layer" + ) from ke + if not isinstance( + _layer, data.DataFrame + ): # TODO: Update type when GeometryDataFrame is implemented. + raise TypeError( + f"Unexpected SOMA type stored in Scene '{scene_id}' obsl layer " + f"'{layer}'" + ) + + # Query the obsl by obs soma_joinid. + return _layer.read((self.obs_joinids(),)) + + def varl(self, scene_id: str, layer: str) -> data.SparseRead: + """Returns as ``varl`` layer from a scene as a sparse read. + + Lifecycle: experimental + """ + raise NotImplementedError() + def to_anndata( self, X_name: str, @@ -578,6 +618,17 @@ def _axism_inner_ndarray( table = self._axism_inner(axis, layer).tables().concat() return self._convert_to_ndarray(axis, table, n_row, n_col) + def _scene(self, scene_id: str) -> scene.Scene: + try: + m_scene = self.experiment.spatial[scene_id] + except KeyError: + raise ValueError(f"Experiment does not contain Scene {scene_id}") from None + if not isinstance(m_scene, scene.Scene): + raise TypeError( + f"Unexpected SOMA type {type(m_scene).__name__} stored in spatial" + ) + return m_scene + @property def _obs_df(self) -> data.DataFrame: return self.experiment.obs @@ -805,6 +856,10 @@ def ms(self) -> Mapping[str, measurement.Measurement]: def obs(self) -> data.DataFrame: ... + @property + def spatial(self) -> Mapping[str, scene.Scene]: + ... + @property def context(self) -> Optional[base_types.ContextBase]: ...