|
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 |
5 | 2 | from warnings import warn |
6 | 3 |
|
7 | 4 | from PIL import Image |
8 | 5 | import biocframe |
9 | 6 | import biocutils as ut |
10 | 7 | from summarizedexperiment.RangedSummarizedExperiment import GRangesOrGRangesList |
11 | 8 | from summarizedexperiment._frameutils import _sanitize_frame |
| 9 | +from summarizedexperiment.BaseSE import _guess_assay_shape |
12 | 10 | from singlecellexperiment import SingleCellExperiment |
13 | 11 | from .SpatialImage import SpatialImage |
14 | 12 |
|
@@ -87,6 +85,7 @@ def __init__( |
87 | 85 | columns of the matrices in assays. For instances of the |
88 | 86 | ``SpatialExperiment`` class, the sample data must include |
89 | 87 | a column named `sample_id`. |
| 88 | + # TODO: add details about default 'sample_id' as 'sample01' |
90 | 89 |
|
91 | 90 | Sample information is coerced to a |
92 | 91 | :py:class:`~biocframe.BiocFrame.BiocFrame`. Defaults to None. |
@@ -152,18 +151,28 @@ def __init__( |
152 | 151 | validate: |
153 | 152 | Internal use only. |
154 | 153 | """ |
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 | + ) |
157 | 162 |
|
158 | 163 | if column_data is None: |
159 | 164 | column_data = biocframe.BiocFrame({"sample_id": []}) |
160 | 165 |
|
| 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) |
161 | 171 | _validate_column_data(column_data=column_data, img_data=img_data) |
| 172 | + _validate_spatial_coords(spatial_coords=spatial_coords, column_data=column_data) |
162 | 173 |
|
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 |
167 | 176 |
|
168 | 177 | super().__init__( |
169 | 178 | assays=assays, |
@@ -441,9 +450,7 @@ def set_spatial_coordinates_names( |
441 | 450 | Returns: |
442 | 451 | A modified ``SpatialExperiment`` object, either as a copy of the original or as a reference to the (in-place-modified) original. |
443 | 452 | """ |
444 | | - _validate_spatial_coords_names( |
445 | | - spatial_coords_names, self.spatial_coordinates |
446 | | - ) |
| 453 | + _validate_spatial_coords_names(spatial_coords_names, self.spatial_coordinates) |
447 | 454 |
|
448 | 455 | old_spatial_coordinates = self.get_spatial_coordinates() |
449 | 456 | new_spatial_coordinates = old_spatial_coordinates.set_column_names( |
@@ -598,20 +605,67 @@ def set_column_data( |
598 | 605 | Returns: |
599 | 606 | A modified ``SpatialExperiment`` object, either as a copy of the original or as a reference to the (in-place-modified) original. |
600 | 607 | """ |
| 608 | + # TODO: remove this because it is handled in the else case already |
601 | 609 | if _column_data is None: |
602 | | - column_data = self.column_data[['symbol']] |
| 610 | + column_data = self.column_data[["symbol"]] |
603 | 611 |
|
604 | 612 | else: |
| 613 | + # TODO: always pass in num_rows to _sanitize_frame |
605 | 614 | column_data = _sanitize_frame(_column_data) |
606 | 615 | if "sample_id" not in column_data.columns: |
607 | 616 | column_data["sample_id"] = self.column_data["sample_id"] |
608 | 617 | else: |
| 618 | + # TODO: move out of else; should always validate no matter what |
609 | 619 | _validate_column_data(column_data=column_data, img_data=self.img_data) |
610 | 620 |
|
611 | 621 | output = self._define_output(in_place) |
612 | 622 | output._column_data = column_data |
613 | 623 | return output |
614 | 624 |
|
| 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 | + |
615 | 669 | ################################ |
616 | 670 | ######>> img_data funcs <<###### |
617 | 671 | ################################ |
@@ -761,7 +815,7 @@ def add_img( |
761 | 815 | ) |
762 | 816 | new_img_data = self._img_data.combine_rows(new_row) |
763 | 817 |
|
764 | | - self.__init__( |
| 818 | + return self.__init__( |
765 | 819 | assays=self.get_assays(), |
766 | 820 | row_ranges=self.get_row_ranges(), |
767 | 821 | row_data=self.get_row_data(), |
|
0 commit comments