Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.15.6
rev: v0.16.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
if __name__ == "__main__":
try:
setup(use_scm_version={"version_scheme": "no-guess-dev"})
except: # noqa
except:
print(
"\n\nAn error occurred while building the project, "
"please ensure you have the most updated version of setuptools, "
Expand Down
83 changes: 42 additions & 41 deletions src/multiassayexperiment/MultiAssayExperiment.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

from collections import OrderedDict, namedtuple
from typing import Any, Dict, List, Optional, Sequence, Union
from collections.abc import Sequence
from typing import Any
from warnings import warn

import biocframe
Expand Down Expand Up @@ -133,10 +134,10 @@ class MultiAssayExperiment(ut.BiocObject):

def __init__(
self,
experiments: Dict[str, Any],
column_data: Optional[biocframe.BiocFrame] = None,
sample_map: Optional[biocframe.BiocFrame] = None,
metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None,
experiments: dict[str, Any],
column_data: biocframe.BiocFrame | None = None,
sample_map: biocframe.BiocFrame | None = None,
metadata: dict[str, Any] | ut.NamedList | None = None,
_validate: bool = True,
) -> None:
"""Initialize an instance of ``MultiAssayExperiment``.
Expand Down Expand Up @@ -293,7 +294,7 @@ def __str__(self) -> str:
expt_name = self.experiment_names[idx]
expt = self._experiments[expt_name]
output += (
f"[{idx}] {expt_name}: {type(expt).__name__} with {expt.shape[0]} rows and {expt.shape[1]} columns \n" # noqa
f"[{idx}] {expt_name}: {type(expt).__name__} with {expt.shape[0]} rows and {expt.shape[1]} columns \n"
)

output += f"column_data columns({len(self._column_data.column_names)}): "
Expand All @@ -310,7 +311,7 @@ def __str__(self) -> str:
######>> experiments <<######
#############################

def get_experiments(self) -> Dict[str, Any]:
def get_experiments(self) -> dict[str, Any]:
"""Access experiments.

Returns:
Expand All @@ -320,7 +321,7 @@ def get_experiments(self) -> Dict[str, Any]:

return self._experiments

def set_experiments(self, experiments: Dict[str, Any], in_place: bool = False) -> MultiAssayExperiment:
def set_experiments(self, experiments: dict[str, Any], in_place: bool = False) -> MultiAssayExperiment:
"""Set new experiments.

Args:
Expand Down Expand Up @@ -350,14 +351,14 @@ def set_experiments(self, experiments: Dict[str, Any], in_place: bool = False) -
@property
def experiments(
self,
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""Alias for :py:meth:`~get_experiments`."""
return self.get_experiments()

@experiments.setter
def experiments(
self,
experiments: Dict[str, Any],
experiments: dict[str, Any],
):
"""Alias for :py:meth:`~set_experiments` with ``in_place = True``.

Expand All @@ -370,23 +371,23 @@ def experiments(
self.set_experiments(experiments, in_place=True)

@property
def assays(self) -> Dict[str, Any]:
def assays(self) -> dict[str, Any]:
"""Alias for :py:meth:`~get_experiments`."""
return self.get_experiments()

##################################
######>> experiment names <<######
##################################

def get_experiment_names(self) -> List[str]:
def get_experiment_names(self) -> list[str]:
"""Get experiment names.

Returns:
List of experiment names.
"""
return list(self._experiments.keys())

def set_experiment_names(self, names: List[str], in_place: bool = False) -> MultiAssayExperiment:
def set_experiment_names(self, names: list[str], in_place: bool = False) -> MultiAssayExperiment:
"""Replace :py:attr:`~experiments`'s names.

Args:
Expand All @@ -413,12 +414,12 @@ def set_experiment_names(self, names: List[str], in_place: bool = False) -> Mult
return output

@property
def experiment_names(self) -> List[str]:
def experiment_names(self) -> list[str]:
"""Alias for :py:meth:`~get_experiment_names`."""
return self.get_experiment_names()

@experiment_names.setter
def experiment_names(self, names: List[str]):
def experiment_names(self, names: list[str]):
"""Alias for :py:meth:`~set_experiment_names` with ``in_place = True``.

As this mutates the original object, a warning is raised.
Expand All @@ -433,7 +434,7 @@ def experiment_names(self, names: List[str]):
######>> experiment accessor <<######
#####################################

def experiment(self, name: Union[int, str], with_sample_data: bool = False) -> Any:
def experiment(self, name: int | str, with_sample_data: bool = False) -> Any:
"""Get an experiment by name.

Args:
Expand Down Expand Up @@ -489,7 +490,7 @@ def experiment(self, name: Union[int, str], with_sample_data: bool = False) -> A

return expt

def get_experiment(self, name: Union[int, str], with_sample_data: bool = False) -> Any:
def get_experiment(self, name: int | str, with_sample_data: bool = False) -> Any:
"""Alias for :py:meth:`~experiment`."""
return self.experiment(name=name, with_sample_data=with_sample_data)

Expand Down Expand Up @@ -606,14 +607,14 @@ def column_data(self, column_data: biocframe.BiocFrame):
######>> subset <<#######
#########################

def _normalize_column_slice(self, columns: Union[str, int, bool, Sequence, slice]):
def _normalize_column_slice(self, columns: str | int | bool | Sequence | slice):
_scalar = None
if columns != slice(None):
columns, _scalar = ut.normalize_subscript(columns, len(self._column_data), self._column_data.row_names)

return columns, _scalar

def _filter_sample_map(self, columns: Union[str, int, bool, Sequence, slice]):
def _filter_sample_map(self, columns: str | int | bool | Sequence | slice):
_samples_to_filter = self._column_data[columns,].row_names

column_names_to_keep = {}
Expand All @@ -628,10 +629,10 @@ def _filter_sample_map(self, columns: Union[str, int, bool, Sequence, slice]):

def subset_experiments(
self,
rows: Optional[Union[str, int, bool, Sequence]],
columns: Optional[Union[str, int, bool, Sequence]],
experiment_names: Union[str, int, bool, Sequence],
) -> Dict[str, Any]:
rows: str | int | bool | Sequence | None,
columns: str | int | bool | Sequence | None,
experiment_names: str | int | bool | Sequence,
) -> dict[str, Any]:
"""Subset experiments.

Args:
Expand Down Expand Up @@ -697,9 +698,9 @@ def subset_experiments(

def _generic_slice(
self,
rows: Optional[Union[str, int, bool, Sequence]] = None,
columns: Optional[Union[str, int, bool, Sequence]] = None,
experiments: Optional[Union[str, int, bool, Sequence]] = None,
rows: str | int | bool | Sequence | None = None,
columns: str | int | bool | Sequence | None = None,
experiments: str | int | bool | Sequence | None = None,
) -> SlicerResult:
"""Slice ``MultiAssayExperiment`` along the rows and/or columns, based on their indices or names.

Expand Down Expand Up @@ -763,7 +764,7 @@ def _generic_slice(

return SlicerResult(_new_experiments, _new_sample_map, _new_column_data)

def subset_by_experiments(self, experiments: Union[str, int, bool, Sequence]) -> MultiAssayExperiment:
def subset_by_experiments(self, experiments: str | int | bool | Sequence) -> MultiAssayExperiment:
"""Subset by experiment(s).

Args:
Expand All @@ -782,7 +783,7 @@ def subset_by_experiments(self, experiments: Union[str, int, bool, Sequence]) ->
sresult = self._generic_slice(experiments=experiments)
return MultiAssayExperiment(sresult.experiments, sresult.column_data, sresult.sample_map, self.metadata)

def subset_by_row(self, rows: Union[str, int, bool, Sequence]) -> MultiAssayExperiment:
def subset_by_row(self, rows: str | int | bool | Sequence) -> MultiAssayExperiment:
"""Subset by rows.

Args:
Expand All @@ -799,7 +800,7 @@ def subset_by_row(self, rows: Union[str, int, bool, Sequence]) -> MultiAssayExpe
sresult = self._generic_slice(rows=rows)
return MultiAssayExperiment(sresult.experiments, sresult.column_data, sresult.sample_map, self.metadata)

def subset_by_column(self, columns: Union[str, int, bool, Sequence]) -> MultiAssayExperiment:
def subset_by_column(self, columns: str | int | bool | Sequence) -> MultiAssayExperiment:
"""Subset by column.

Args:
Expand Down Expand Up @@ -864,7 +865,7 @@ def __getitem__(self, args: tuple) -> MultiAssayExperiment:
)
else:
raise ValueError(
f"`{type(self).__name__}` only supports 3-dimensional slicing along rows, columns and/or experiments." # noqa
f"`{type(self).__name__}` only supports 3-dimensional slicing along rows, columns and/or experiments."
)

raise TypeError("'args' must be a tuple")
Expand Down Expand Up @@ -894,7 +895,7 @@ def complete_cases(self) -> Sequence[bool]:

return vec

def replicated(self) -> Dict[str, Dict[str, Sequence[bool]]]:
def replicated(self) -> dict[str, dict[str, Sequence[bool]]]:
"""Identify samples with replicates within each experiment.

Returns:
Expand Down Expand Up @@ -927,7 +928,7 @@ def replicated(self) -> Dict[str, Dict[str, Sequence[bool]]]:

return replicates

def find_common_row_names(self) -> List[str]:
def find_common_row_names(self) -> list[str]:
"""Finds common row names across all experiments."""

_common = None
Expand Down Expand Up @@ -958,7 +959,7 @@ def intersect_rows(self) -> MultiAssayExperiment:
######>> row or column names <<#####
####################################

def get_row_names(self) -> Dict[str, Optional[ut.Names]]:
def get_row_names(self) -> dict[str, ut.Names | None]:
"""
Returns:
Dictionary, with experiment names as keys, and row names as values.
Expand All @@ -970,11 +971,11 @@ def get_row_names(self) -> Dict[str, Optional[ut.Names]]:
return _all_row_names

@property
def rownames(self) -> Dict[str, Optional[ut.Names]]:
def rownames(self) -> dict[str, ut.Names | None]:
"""Alias for :py:attr:`~get_row_names`, provided for back-compatibility."""
return self.get_row_names()

def get_column_names(self) -> Dict[str, Optional[ut.Names]]:
def get_column_names(self) -> dict[str, ut.Names | None]:
"""
Returns:
Dictionary, with experiment names as keys, and the column names as values.
Expand All @@ -986,17 +987,17 @@ def get_column_names(self) -> Dict[str, Optional[ut.Names]]:
return _all_row_names

@property
def columnnames(self) -> Dict[str, Optional[ut.Names]]:
def columnnames(self) -> dict[str, ut.Names | None]:
"""Alias for :py:attr:`~get_column_names`, provided for back-compatibility."""
return self.get_column_names()

@property
def colnames(self) -> Dict[str, Optional[ut.Names]]:
def colnames(self) -> dict[str, ut.Names | None]:
"""Alias for :py:attr:`~get_column_names`, provided for back-compatibility."""
return self.get_column_names()

@property
def column_names(self) -> Dict[str, Optional[ut.Names]]:
def column_names(self) -> dict[str, ut.Names | None]:
"""Alias for :py:attr:`~get_column_names`, provided for back-compatibility."""
return self.get_column_names()

Expand All @@ -1009,7 +1010,7 @@ def add_experiment(
name: str,
experiment: Any,
sample_map: biocframe.BiocFrame,
column_data: Optional[biocframe.BiocFrame] = None,
column_data: biocframe.BiocFrame | None = None,
in_place: bool = False,
) -> MultiAssayExperiment:
"""Add a new experiment to `MultiAssayExperiment`.
Expand Down Expand Up @@ -1106,7 +1107,7 @@ def to_mudata(self):
return MuData(exptsList)

@classmethod
def from_mudata(cls, input: "mudata.MuData") -> MultiAssayExperiment:
def from_mudata(cls, input: mudata.MuData) -> MultiAssayExperiment:
"""Create a ``MultiAssayExperiment`` object from :py:class:`~mudata.MuData`.

The import naively creates sample mapping, each ``experiment`` is considered to be a `sample`.
Expand Down Expand Up @@ -1166,7 +1167,7 @@ def from_mudata(cls, input: "mudata.MuData") -> MultiAssayExperiment:
)

@classmethod
def from_anndata(cls, input: "anndata.AnnData", name: str = "unknown") -> MultiAssayExperiment:
def from_anndata(cls, input: anndata.AnnData, name: str = "unknown") -> MultiAssayExperiment:
"""Create a ``MultiAssayExperiment`` from :py:class:`~anndata.AnnData`.

Since :py:class:`~anndata.AnnData` does not contain sample information,
Expand Down
2 changes: 1 addition & 1 deletion src/multiassayexperiment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
finally:
del version, PackageNotFoundError

from .io import read_h5ad, make_mae
from .io import make_mae, read_h5ad
from .MultiAssayExperiment import MultiAssayExperiment
6 changes: 3 additions & 3 deletions src/multiassayexperiment/io/interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from collections import OrderedDict
from typing import Any, Dict
from typing import Any

from ..MultiAssayExperiment import MultiAssayExperiment, _create_smap_from_experiments

Expand All @@ -8,7 +8,7 @@
__license__ = "MIT"


def make_mae(experiments: Dict[str, Any]) -> MultiAssayExperiment:
def make_mae(experiments: dict[str, Any]) -> MultiAssayExperiment:
"""Create an :py:class:`~multiassayexperiment.MultiAssayExperiment.MultiAssayExperiment` from a dictionary of
experiment objects. Each experiment is either an :py:class:`~anndata.AnnData` object or a subclass of
:py:class:`~summarizedexperiment.SummarizedExperiment.SummarizedExperiment`. :py:class:`~anndata.AnnData` objects
Expand Down Expand Up @@ -37,8 +37,8 @@ def make_mae(experiments: Dict[str, Any]) -> MultiAssayExperiment:
Returns:
An MAE from the experiments.
"""
from singlecellexperiment import SingleCellExperiment
from anndata import AnnData
from singlecellexperiment import SingleCellExperiment
from summarizedexperiment import SummarizedExperiment

if not isinstance(experiments, dict):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_add_expt.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"starts": range(100, 300),
"ends": range(110, 310),
"strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"] * 20,
"score": range(0, 200),
"score": range(200),
"GC": [random() for _ in range(10)] * 20,
}
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"starts": range(100, 300),
"ends": range(110, 310),
"strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"] * 20,
"score": range(0, 200),
"score": range(200),
"GC": [random() for _ in range(10)] * 20,
}
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"starts": range(100, 300),
"ends": range(110, 310),
"strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"] * 20,
"score": range(0, 200),
"score": range(200),
"GC": [random() for _ in range(10)] * 20,
}
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_with_coldata.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"starts": range(100, 300),
"ends": range(110, 310),
"strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"] * 20,
"score": range(0, 200),
"score": range(200),
"GC": [random() for _ in range(10)] * 20,
}
)
Expand Down
Loading