Skip to content

Commit 43f749e

Browse files
authored
Classes extend BiocObject from biocutils. (#47)
1 parent 70e327a commit 43f749e

3 files changed

Lines changed: 33 additions & 76 deletions

File tree

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
test:
2929
strategy:
3030
matrix:
31-
python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
31+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
3232
platform:
3333
- ubuntu-latest
3434
- macos-latest

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Version 0.6.0
4+
5+
- Class extends `BiocObject` from biocutils.
6+
- Github actions run from 3.10-3.14.
7+
- `validate` renamed to `_validate` for consistency.
8+
39
## Version 0.5.0 - 0.5.2
410

511
- chore: Remove Python 3.8 (EOL)

src/multiassayexperiment/MultiAssayExperiment.py

Lines changed: 26 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from collections import OrderedDict, namedtuple
24
from typing import Any, Dict, List, Optional, Sequence, Union
35
from warnings import warn
@@ -15,7 +17,7 @@
1517

1618
def _sanitize_frame(frame):
1719
if se._frameutils.is_pandas(frame):
18-
frame = biocframe.from_pandas(frame)
20+
frame = biocframe.BiocFrame.from_pandas(frame)
1921

2022
return frame
2123

@@ -121,7 +123,7 @@ def _create_smap_from_experiments(experiments):
121123
return col_data, sample_map
122124

123125

124-
class MultiAssayExperiment:
126+
class MultiAssayExperiment(ut.BiocObject):
125127
"""Container class for representing and managing multi-omics genomic experiments.
126128
127129
Checkout the
@@ -132,10 +134,10 @@ class MultiAssayExperiment:
132134
def __init__(
133135
self,
134136
experiments: Dict[str, Any],
135-
column_data: biocframe.BiocFrame = None,
136-
sample_map: biocframe.BiocFrame = None,
137-
metadata: Optional[dict] = None,
138-
validate: bool = True,
137+
column_data: Optional[biocframe.BiocFrame] = None,
138+
sample_map: Optional[biocframe.BiocFrame] = None,
139+
metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None,
140+
_validate: bool = True,
139141
) -> None:
140142
"""Initialize an instance of ``MultiAssayExperiment``.
141143
@@ -194,11 +196,12 @@ def __init__(
194196
metadata:
195197
Additional study-level metadata. Defaults to None.
196198
197-
validate:
199+
_validate:
198200
Internal use only.
199201
"""
202+
super().__init__(metadata=metadata, _validate=_validate)
203+
200204
self._experiments = experiments if experiments is not None else {}
201-
self._metadata = metadata if metadata is not None else {}
202205

203206
if sample_map is not None and column_data is not None:
204207
self._sample_map = _sanitize_frame(sample_map)
@@ -211,17 +214,11 @@ def __init__(
211214
"Either 'sample_map' or 'column_data' is `None`. Either both should be provided or set both to `None`."
212215
)
213216

214-
if validate:
217+
if _validate:
215218
_validate_experiments(self._experiments)
216219
_validate_column_data(self._column_data)
217220
_validate_sample_map(self._sample_map, self._column_data, self._experiments)
218221

219-
def _define_output(self, in_place: bool = False) -> "MultiAssayExperiment":
220-
if in_place is True:
221-
return self
222-
else:
223-
return self.__copy__()
224-
225222
#########################
226223
######>> Copying <<######
227224
#########################
@@ -244,6 +241,7 @@ def __deepcopy__(self, memo=None, _nil=[]):
244241
column_data=_column_data_copy,
245242
sample_map=_sample_map_copy,
246243
metadata=_metadata_copy,
244+
_validate=False,
247245
)
248246

249247
def __copy__(self):
@@ -257,6 +255,7 @@ def __copy__(self):
257255
column_data=self._column_data,
258256
sample_map=self._sample_map,
259257
metadata=self._metadata,
258+
_validate=False,
260259
)
261260

262261
def copy(self):
@@ -321,7 +320,7 @@ def get_experiments(self) -> Dict[str, Any]:
321320

322321
return self._experiments
323322

324-
def set_experiments(self, experiments: Dict[str, Any], in_place: bool = False) -> "MultiAssayExperiment":
323+
def set_experiments(self, experiments: Dict[str, Any], in_place: bool = False) -> MultiAssayExperiment:
325324
"""Set new experiments.
326325
327326
Args:
@@ -387,7 +386,7 @@ def get_experiment_names(self) -> List[str]:
387386
"""
388387
return list(self._experiments.keys())
389388

390-
def set_experiment_names(self, names: List[str], in_place: bool = False) -> "MultiAssayExperiment":
389+
def set_experiment_names(self, names: List[str], in_place: bool = False) -> MultiAssayExperiment:
391390
"""Replace :py:attr:`~experiments`'s names.
392391
393392
Args:
@@ -513,7 +512,7 @@ def get_sample_map(self) -> biocframe.BiocFrame:
513512
"""
514513
return self._sample_map
515514

516-
def set_sample_map(self, sample_map: biocframe.BiocFrame, in_place: bool = False) -> "MultiAssayExperiment":
515+
def set_sample_map(self, sample_map: biocframe.BiocFrame, in_place: bool = False) -> MultiAssayExperiment:
517516
"""Set new sample mapping.
518517
519518
Args:
@@ -563,7 +562,7 @@ def get_column_data(self) -> biocframe.BiocFrame:
563562
"""
564563
return self._column_data
565564

566-
def set_column_data(self, column_data: biocframe.BiocFrame, in_place: bool = False) -> "MultiAssayExperiment":
565+
def set_column_data(self, column_data: biocframe.BiocFrame, in_place: bool = False) -> MultiAssayExperiment:
567566
"""Set new sample metadata.
568567
569568
Args:
@@ -603,54 +602,6 @@ def column_data(self, column_data: biocframe.BiocFrame):
603602
)
604603
self.set_column_data(column_data, in_place=True)
605604

606-
###########################
607-
######>> metadata <<#######
608-
###########################
609-
610-
def get_metadata(self) -> dict:
611-
"""
612-
Returns:
613-
Dictionary of metadata for this object.
614-
"""
615-
return self._metadata
616-
617-
def set_metadata(self, metadata: dict, in_place: bool = False) -> "MultiAssayExperiment":
618-
"""Set additional metadata.
619-
620-
Args:
621-
metadata:
622-
New metadata for this object.
623-
624-
in_place:
625-
Whether to modify the ``MultiAssayExperiment`` in place.
626-
627-
Returns:
628-
A modified ``MultiAssayExperiment`` object, either as a copy of the original
629-
or as a reference to the (in-place-modified) original.
630-
"""
631-
if not isinstance(metadata, dict):
632-
raise TypeError(f"`metadata` must be a dictionary, provided {type(metadata)}.")
633-
output = self._define_output(in_place)
634-
output._metadata = metadata
635-
return output
636-
637-
@property
638-
def metadata(self) -> dict:
639-
"""Alias for :py:attr:`~get_metadata`."""
640-
return self.get_metadata()
641-
642-
@metadata.setter
643-
def metadata(self, metadata: dict):
644-
"""Alias for :py:attr:`~set_metadata` with ``in_place = True``.
645-
646-
As this mutates the original object, a warning is raised.
647-
"""
648-
warn(
649-
"Setting property 'metadata' is an in-place operation, use 'set_metadata' instead",
650-
UserWarning,
651-
)
652-
self.set_metadata(metadata, in_place=True)
653-
654605
#########################
655606
######>> subset <<#######
656607
#########################
@@ -812,7 +763,7 @@ def _generic_slice(
812763

813764
return SlicerResult(_new_experiments, _new_sample_map, _new_column_data)
814765

815-
def subset_by_experiments(self, experiments: Union[str, int, bool, Sequence]) -> "MultiAssayExperiment":
766+
def subset_by_experiments(self, experiments: Union[str, int, bool, Sequence]) -> MultiAssayExperiment:
816767
"""Subset by experiment(s).
817768
818769
Args:
@@ -831,7 +782,7 @@ def subset_by_experiments(self, experiments: Union[str, int, bool, Sequence]) ->
831782
sresult = self._generic_slice(experiments=experiments)
832783
return MultiAssayExperiment(sresult.experiments, sresult.column_data, sresult.sample_map, self.metadata)
833784

834-
def subset_by_row(self, rows: Union[str, int, bool, Sequence]) -> "MultiAssayExperiment":
785+
def subset_by_row(self, rows: Union[str, int, bool, Sequence]) -> MultiAssayExperiment:
835786
"""Subset by rows.
836787
837788
Args:
@@ -848,7 +799,7 @@ def subset_by_row(self, rows: Union[str, int, bool, Sequence]) -> "MultiAssayExp
848799
sresult = self._generic_slice(rows=rows)
849800
return MultiAssayExperiment(sresult.experiments, sresult.column_data, sresult.sample_map, self.metadata)
850801

851-
def subset_by_column(self, columns: Union[str, int, bool, Sequence]) -> "MultiAssayExperiment":
802+
def subset_by_column(self, columns: Union[str, int, bool, Sequence]) -> MultiAssayExperiment:
852803
"""Subset by column.
853804
854805
Args:
@@ -865,7 +816,7 @@ def subset_by_column(self, columns: Union[str, int, bool, Sequence]) -> "MultiAs
865816
sresult = self._generic_slice(columns=columns)
866817
return MultiAssayExperiment(sresult.experiments, sresult.column_data, sresult.sample_map, self.metadata)
867818

868-
def __getitem__(self, args: tuple) -> "MultiAssayExperiment":
819+
def __getitem__(self, args: tuple) -> MultiAssayExperiment:
869820
"""Subset a `MultiAssayExperiment`.
870821
871822
Args:
@@ -988,7 +939,7 @@ def find_common_row_names(self) -> List[str]:
988939

989940
return _common
990941

991-
def intersect_rows(self) -> "MultiAssayExperiment":
942+
def intersect_rows(self) -> MultiAssayExperiment:
992943
"""Finds common row names across all experiments and filters the MAE to these rows.
993944
994945
Returns:
@@ -1060,7 +1011,7 @@ def add_experiment(
10601011
sample_map: biocframe.BiocFrame,
10611012
column_data: Optional[biocframe.BiocFrame] = None,
10621013
in_place: bool = False,
1063-
) -> "MultiAssayExperiment":
1014+
) -> MultiAssayExperiment:
10641015
"""Add a new experiment to `MultiAssayExperiment`.
10651016
10661017
``sample_map`` must be provided to map the columns from this experiment to
@@ -1155,7 +1106,7 @@ def to_mudata(self):
11551106
return MuData(exptsList)
11561107

11571108
@classmethod
1158-
def from_mudata(cls, input: "mudata.MuData") -> "MultiAssayExperiment":
1109+
def from_mudata(cls, input: "mudata.MuData") -> MultiAssayExperiment:
11591110
"""Create a ``MultiAssayExperiment`` object from :py:class:`~mudata.MuData`.
11601111
11611112
The import naively creates sample mapping, each ``experiment`` is considered to be a `sample`.
@@ -1215,7 +1166,7 @@ def from_mudata(cls, input: "mudata.MuData") -> "MultiAssayExperiment":
12151166
)
12161167

12171168
@classmethod
1218-
def from_anndata(cls, input: "anndata.AnnData", name: str = "unknown") -> "MultiAssayExperiment":
1169+
def from_anndata(cls, input: "anndata.AnnData", name: str = "unknown") -> MultiAssayExperiment:
12191170
"""Create a ``MultiAssayExperiment`` from :py:class:`~anndata.AnnData`.
12201171
12211172
Since :py:class:`~anndata.AnnData` does not contain sample information,

0 commit comments

Comments
 (0)