Skip to content

Commit c7df689

Browse files
committed
Implement get_slice()
1 parent 1fb5d79 commit c7df689

File tree

2 files changed

+73
-17
lines changed

2 files changed

+73
-17
lines changed

src/spatialexperiment/SpatialExperiment.py

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
# TODO: implement readImgData and read10xVisium?
2-
# TODO: interop w/ SpatialData class from scverse
3-
# TODO: combine methods
4-
from typing import Any, Dict, List, Optional, Union
1+
from typing import Any, Dict, List, Optional, Union, Sequence
52
from warnings import warn
63

74
from PIL import Image
85
import biocframe
96
import biocutils as ut
107
from summarizedexperiment.RangedSummarizedExperiment import GRangesOrGRangesList
118
from summarizedexperiment._frameutils import _sanitize_frame
9+
from summarizedexperiment.BaseSE import _guess_assay_shape
1210
from singlecellexperiment import SingleCellExperiment
1311
from .SpatialImage import SpatialImage
1412

@@ -87,6 +85,7 @@ def __init__(
8785
columns of the matrices in assays. For instances of the
8886
``SpatialExperiment`` class, the sample data must include
8987
a column named `sample_id`.
88+
# TODO: add details about default 'sample_id' as 'sample01'
9089
9190
Sample information is coerced to a
9291
:py:class:`~biocframe.BiocFrame.BiocFrame`. Defaults to None.
@@ -152,18 +151,28 @@ def __init__(
152151
validate:
153152
Internal use only.
154153
"""
155-
_validate_spatial_coords(spatial_coords=spatial_coords, column_data=column_data)
156-
_validate_img_data(img_data=img_data)
154+
# TODO: figure out how to handle the case where `spatial_coords` is not None but `column_data` is None. in this case, `column_data` should have a `sample_id` column with the default value `sample_01`. this might remove the need for _guess_assay_shape().
155+
shape = _guess_assay_shape(
156+
assays=assays if assays is not None else {},
157+
rows=row_data,
158+
cols=column_data,
159+
row_names=row_names,
160+
col_names=column_names,
161+
)
157162

158163
if column_data is None:
159164
column_data = biocframe.BiocFrame({"sample_id": []})
160165

166+
column_data = _sanitize_frame(column_data, num_rows=shape[1])
167+
spatial_coords = _sanitize_frame(spatial_coords, num_rows=shape[1])
168+
img_data = _sanitize_frame(img_data, num_rows=0)
169+
170+
_validate_img_data(img_data=img_data)
161171
_validate_column_data(column_data=column_data, img_data=img_data)
172+
_validate_spatial_coords(spatial_coords=spatial_coords, column_data=column_data)
162173

163-
self._spatial_coords = _sanitize_frame(
164-
spatial_coords, num_rows=column_data.shape[0]
165-
)
166-
self._img_data = _sanitize_frame(img_data, num_rows=0)
174+
self._spatial_coords = spatial_coords
175+
self._img_data = img_data
167176

168177
super().__init__(
169178
assays=assays,
@@ -441,9 +450,7 @@ def set_spatial_coordinates_names(
441450
Returns:
442451
A modified ``SpatialExperiment`` object, either as a copy of the original or as a reference to the (in-place-modified) original.
443452
"""
444-
_validate_spatial_coords_names(
445-
spatial_coords_names, self.spatial_coordinates
446-
)
453+
_validate_spatial_coords_names(spatial_coords_names, self.spatial_coordinates)
447454

448455
old_spatial_coordinates = self.get_spatial_coordinates()
449456
new_spatial_coordinates = old_spatial_coordinates.set_column_names(
@@ -598,20 +605,67 @@ def set_column_data(
598605
Returns:
599606
A modified ``SpatialExperiment`` object, either as a copy of the original or as a reference to the (in-place-modified) original.
600607
"""
608+
# TODO: remove this because it is handled in the else case already
601609
if _column_data is None:
602-
column_data = self.column_data[['symbol']]
610+
column_data = self.column_data[["symbol"]]
603611

604612
else:
613+
# TODO: always pass in num_rows to _sanitize_frame
605614
column_data = _sanitize_frame(_column_data)
606615
if "sample_id" not in column_data.columns:
607616
column_data["sample_id"] = self.column_data["sample_id"]
608617
else:
618+
# TODO: move out of else; should always validate no matter what
609619
_validate_column_data(column_data=column_data, img_data=self.img_data)
610620

611621
output = self._define_output(in_place)
612622
output._column_data = column_data
613623
return output
614624

625+
################################
626+
#########>> slicers <<##########
627+
################################
628+
629+
def get_slice(
630+
self,
631+
rows: Optional[Union[str, int, bool, Sequence]],
632+
columns: Optional[Union[str, int, bool, Sequence]]
633+
) -> "SpatialExperiment":
634+
"""Alias for :py:attr:`~__getitem__`."""
635+
636+
spe = super().get_slice(rows=rows, columns=columns)
637+
638+
slicer = self._generic_slice(rows=rows, columns=columns)
639+
do_slice_cols = not (isinstance(slicer.col_indices, slice) and slicer.col_indices == slice(None))
640+
641+
new_spatial_coords = None
642+
643+
if do_slice_cols:
644+
new_spatial_coords = self.spatial_coords[slicer.col_indices, :]
645+
646+
column_sample_ids = set(self.column_data["sample_id"])
647+
mask = [sample_id in column_sample_ids for sample_id in self.img_data["sample_id"]]
648+
649+
new_img_data = self.img_data[mask]
650+
651+
current_class_const = type(self)
652+
return current_class_const(
653+
assays=spe.assays,
654+
row_ranges=spe.row_ranges,
655+
row_data=spe.row_data,
656+
column_data=spe.column_data,
657+
row_names=spe.row_names,
658+
column_names=spe.column_names,
659+
metadata=spe.metadata,
660+
main_experiment_name=spe.main_experiment_name,
661+
reduced_dims=spe.reduced_dims,
662+
alternative_experiments=spe.alternative_experiments,
663+
row_pairs=spe.row_pairs,
664+
column_pairs=spe.column_pairs,
665+
spatial_coords=new_spatial_coords,
666+
img_data=new_img_data
667+
)
668+
615669
################################
616670
######>> img_data funcs <<######
617671
################################
@@ -761,7 +815,7 @@ def add_img(
761815
)
762816
new_img_data = self._img_data.combine_rows(new_row)
763817

764-
self.__init__(
818+
return self.__init__(
765819
assays=self.get_assays(),
766820
row_ranges=self.get_row_ranges(),
767821
row_data=self.get_row_data(),

src/spatialexperiment/_validators.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def _validate_column_data(column_data, img_data):
2424

2525
if not isinstance(column_data, biocframe.BiocFrame):
2626
raise TypeError("'column_data' must be a BiocFrame object.")
27-
27+
2828
if "sample_id" not in column_data.columns:
2929
raise ValueError(error_message)
3030

@@ -35,7 +35,9 @@ def _validate_column_data(column_data, img_data):
3535
num_unique_sample_ids_provided = len(column_data["sample_id"].unique())
3636

3737
if num_unique_sample_ids != num_unique_sample_ids_provided:
38-
raise ValueError(f"Number of unique 'sample_id's is {num_unique_sample_ids}, but {num_unique_sample_ids_provided} were provided.")
38+
raise ValueError(
39+
f"Number of unique 'sample_id's is {num_unique_sample_ids}, but {num_unique_sample_ids_provided} were provided."
40+
)
3941

4042

4143
def _validate_id(id):

0 commit comments

Comments
 (0)