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.5
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ To create a `SummarizedExperiment`,
from summarizedexperiment import SummarizedExperiment

tse = SummarizedExperiment(
assays={"counts": counts}, row_data=row_data, column_data=col_data,
assays={"counts": counts},
row_data=row_data,
column_data=col_data,
metadata={"seq_platform": "Illumina NovaSeq 6000"},
)
```
Expand All @@ -93,8 +95,10 @@ from summarizedexperiment import RangedSummarizedExperiment
from genomicranges import GenomicRanges

trse = RangedSummarizedExperiment(
assays={"counts": counts}, row_data=row_data,
row_ranges=GenomicRanges.from_pandas(row_data.to_pandas()), column_data=col_data
assays={"counts": counts},
row_data=row_data,
row_ranges=GenomicRanges.from_pandas(row_data.to_pandas()),
column_data=col_data,
)
```

Expand Down
26 changes: 12 additions & 14 deletions docs/extend_se.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ As a simple example, let's create a new class called `BioSampleSE` that stores b
```python
from summarizedexperiment import SummarizedExperiment


class BioSampleSE(SummarizedExperiment):
pass
```
Expand All @@ -25,8 +26,8 @@ from summarizedexperiment import SummarizedExperiment
import biocframe
from typing import Dict, Any, List, Optional

class BioSampleSE(SummarizedExperiment):

class BioSampleSE(SummarizedExperiment):
def __init__(
self,
assays: Dict[str, Any] = None,
Expand All @@ -35,7 +36,7 @@ class BioSampleSE(SummarizedExperiment):
row_names: Optional[List[str]] = None,
column_names: Optional[List[str]] = None,
metadata: Optional[dict] = None,
bio_sample_information: Optional[biocframe.BiocFrame] = None, # NEW SLOT
bio_sample_information: Optional[biocframe.BiocFrame] = None, # NEW SLOT
validate: bool = True,
) -> None:
super().__init__(
Expand Down Expand Up @@ -66,7 +67,6 @@ Our class now validates the new slot:

```python
class BioSampleSE(SummarizedExperiment):

def __init__(
self,
assays: Dict[str, Any] = None,
Expand All @@ -75,7 +75,7 @@ class BioSampleSE(SummarizedExperiment):
row_names: Optional[List[str]] = None,
column_names: Optional[List[str]] = None,
metadata: Optional[dict] = None,
bio_sample_information: Optional[biocframe.BiocFrame] = None, # NEW SLOT
bio_sample_information: Optional[biocframe.BiocFrame] = None, # NEW SLOT
validate: bool = True,
) -> None:
super().__init__(
Expand Down Expand Up @@ -108,6 +108,7 @@ def get_bio_sample_information(self) -> Optional[biocframe.BiocFrame]:
"""
return self._bio_sample_information


def set_bio_sample_information(
self, bio_sample_information: Optional[biocframe.BiocFrame], in_place: bool = False
) -> "BioSampleSE":
Expand All @@ -126,7 +127,7 @@ def set_bio_sample_information(
"""
_validate_bio_sample_information(bio_sample_info)

output = self._define_output(in_place) # MAKES A SHALLOW COPY
output = self._define_output(in_place) # MAKES A SHALLOW COPY
output._bio_sample_information = bio_sample_info
return output
```
Expand All @@ -139,6 +140,7 @@ def bio_sample_information(self) -> biocframe.BiocFrame:
"""Alias for :py:meth:`~get_bio_sample_info`."""
return self.get_bio_sample_info()


@bio_sample_information.setter
def bio_sample_information(self, bio_sample_info: biocframe.BiocFrame) -> None:
"""Alias for :py:meth:`~set_bio_sample_info`."""
Expand All @@ -147,7 +149,6 @@ def bio_sample_information(self, bio_sample_info: biocframe.BiocFrame) -> None:
UserWarning,
)
return self.set_bio_sample_information(row_ranges=row_ranges, in_place=True)

```

This allows users to easily access the new property using the **dot** notation on an instance, for example, `obj.bio_sample_info` provides access to the attribute.
Expand Down Expand Up @@ -183,6 +184,7 @@ def __deepcopy__(self, memo=None, _nil=[]):
metadata=_metadata_copy,
)


def __copy__(self):
"""
Returns:
Expand All @@ -199,6 +201,7 @@ def __copy__(self):
metadata=self._metadata,
)


def copy(self):
"""Alias for :py:meth:`~__copy__`."""
return self.__copy__()
Expand Down Expand Up @@ -241,13 +244,12 @@ def get_slice(


```python

def _validate_bio_sample_information(bio_sample_info):
if not isinstance(bio_sample_info, biocframe.BiocFrame):
raise Exception("Biosample information must be a BiocFrame object.")

class BioSampleSE(SummarizedExperiment):

class BioSampleSE(SummarizedExperiment):
def __init__(
self,
assays: Dict[str, Any] = None,
Expand All @@ -256,7 +258,7 @@ class BioSampleSE(SummarizedExperiment):
row_names: Optional[List[str]] = None,
column_names: Optional[List[str]] = None,
metadata: Optional[dict] = None,
bio_sample_information: Optional[biocframe.BiocFrame] = None, # NEW SLOT
bio_sample_information: Optional[biocframe.BiocFrame] = None, # NEW SLOT
validate: bool = True,
) -> None:
super().__init__(
Expand Down Expand Up @@ -320,7 +322,6 @@ class BioSampleSE(SummarizedExperiment):
"""Alias for :py:meth:`~__copy__`."""
return self.__copy__()


def get_bio_sample_information(self) -> Optional[biocframe.BiocFrame]:
"""Get biosample information.

Expand All @@ -347,11 +348,10 @@ class BioSampleSE(SummarizedExperiment):
"""
_validate_bio_sample_information(bio_sample_info)

output = self._define_output(in_place) # MAKES A SHALLOW COPY
output = self._define_output(in_place) # MAKES A SHALLOW COPY
output._bio_sample_information = bio_sample_info
return output


@property
def bio_sample_information(self) -> biocframe.BiocFrame:
"""Alias for :py:meth:`~get_bio_sample_info`."""
Expand All @@ -365,8 +365,6 @@ class BioSampleSE(SummarizedExperiment):
UserWarning,
)
return self.set_bio_sample_information(row_ranges=row_ranges, in_place=True)


```

That's the minimum required to extend a `SummarizedExperiment` and adapt it to new use cases. Please follow the [developer guide](https://github.com/BiocPy/developer_guide), which provides information on class design, package setup, and documentation to ensure consistency in how BiocPy-related packages are developed.
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
56 changes: 27 additions & 29 deletions src/summarizedexperiment/RangedSummarizedExperiment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

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

import biocframe
Expand Down Expand Up @@ -92,13 +93,13 @@ class RangedSummarizedExperiment(SummarizedExperiment):

def __init__(
self,
assays: Dict[str, Any] = None,
row_ranges: Optional[GRangesOrGRangesList] = None,
row_data: Optional[biocframe.BiocFrame] = None,
column_data: Optional[biocframe.BiocFrame] = None,
row_names: Optional[List[str]] = None,
column_names: Optional[List[str]] = None,
metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None,
assays: dict[str, Any] = None,
row_ranges: GRangesOrGRangesList | None = None,
row_data: biocframe.BiocFrame | None = None,
column_data: biocframe.BiocFrame | None = None,
row_names: list[str] | None = None,
column_names: list[str] | None = None,
metadata: dict[str, Any] | ut.NamedList | None = None,
_validate: bool = True,
) -> None:
"""Initialize a `RangedSummarizedExperiment` (RSE) object.
Expand Down Expand Up @@ -274,7 +275,7 @@ def __str__(self) -> str:
)
output += f"column_names({0 if self._column_names is None else len(self._column_names)}): {' ' if self._column_names is None else ut.print_truncated_list(self._column_names)}\n"

output += f"metadata({str(len(self.metadata))}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}"
output += f"metadata({len(self.metadata)!s}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}"

return output

Expand All @@ -291,7 +292,7 @@ def get_row_ranges(self) -> GRangesOrGRangesList:
return self._row_ranges

def set_row_ranges(
self, row_ranges: Optional[GRangesOrGRangesList], in_place: bool = False
self, row_ranges: GRangesOrGRangesList | None, in_place: bool = False
) -> RangedSummarizedExperiment:
"""Set new genomic features.

Expand Down Expand Up @@ -351,7 +352,7 @@ def start(self) -> np.ndarray:
return self.row_ranges.start

@property
def seqnames(self) -> List[str]:
def seqnames(self) -> list[str]:
"""Get sequence or chromosome names.

Returns:
Expand Down Expand Up @@ -392,21 +393,18 @@ def seq_info(self) -> SeqInfo:

# rest of them are inherited from BaseSE.

def _normalize_row_slice(self, rows: Union[str, int, bool, Sequence]):
def _normalize_row_slice(self, rows: str | int | bool | Sequence):

if isinstance(rows, (GenomicRanges, CompressedGenomicRangesList)):
hits = self.row_ranges.find_overlaps(query=rows)
rows = hits.get_column("self_hits")
elif hasattr(rows, "find_overlaps"):
if isinstance(rows, (GenomicRanges, CompressedGenomicRangesList)) or hasattr(rows, "find_overlaps"):
hits = self.row_ranges.find_overlaps(query=rows)
rows = hits.get_column("self_hits")

return super()._normalize_row_slice(rows)

def get_slice(
self,
rows: Optional[Union[str, int, bool, Sequence]],
columns: Optional[Union[str, int, bool, Sequence]],
rows: str | int | bool | Sequence | None,
columns: str | int | bool | Sequence | None,
) -> RangedSummarizedExperiment:
"""Alias for :py:attr:`~__getitem__`, for back-compatibility."""

Expand All @@ -431,7 +429,7 @@ def get_slice(
######>> range ops <<#######
############################

def coverage(self, shift: int = 0, width: Optional[int] = None, weight: int = 1) -> Dict[str, np.ndarray]:
def coverage(self, shift: int = 0, width: int | None = None, weight: int = 1) -> dict[str, np.ndarray]:
"""Calculate coverage for each chromosome.

Args:
Expand All @@ -456,7 +454,7 @@ def nearest(
query: GRangesOrRangeSE,
select: Literal["all", "arbitrary"] = "all",
ignore_strand: bool = False,
) -> Optional[List[Optional[int]]]:
) -> list[int | None] | None:
"""Search nearest positions both upstream and downstream that overlap with each range in ``query``.

Args:
Expand Down Expand Up @@ -496,7 +494,7 @@ def precede(
query: GRangesOrRangeSE,
select: Literal["all", "arbitrary"] = "all",
ignore_strand: bool = False,
) -> Optional[List[Optional[int]]]:
) -> list[int | None] | None:
"""Search nearest positions only downstream that overlap with each range in ``query``.

Args:
Expand Down Expand Up @@ -536,7 +534,7 @@ def follow(
query: GRangesOrRangeSE,
select: Literal["all", "arbitrary"] = "all",
ignore_strand: bool = False,
) -> Optional[List[Optional[int]]]:
) -> list[int | None] | None:
"""Search nearest positions only upstream that overlap with each range in ``query``.

Args:
Expand Down Expand Up @@ -617,7 +615,7 @@ def flank(

def resize(
self,
width: Union[int, List[int], np.ndarray],
width: int | list[int] | np.ndarray,
fix: Literal["start", "end", "center"] = "start",
ignore_strand: bool = False,
in_place: bool = False,
Expand Down Expand Up @@ -654,7 +652,7 @@ def resize(
output._row_ranges = new_ranges
return output

def shift(self, shift: Union[int, List[int], np.ndarray] = 0, in_place: bool = False) -> RangedSummarizedExperiment:
def shift(self, shift: int | list[int] | np.ndarray = 0, in_place: bool = False) -> RangedSummarizedExperiment:
"""Shift all intervals.

``shift`` may be be negative.
Expand Down Expand Up @@ -708,8 +706,8 @@ def promoters(

def restrict(
self,
start: Optional[Union[int, List[int], np.ndarray]] = None,
end: Optional[Union[int, List[int], np.ndarray]] = None,
start: int | list[int] | np.ndarray | None = None,
end: int | list[int] | np.ndarray | None = None,
keep_all_ranges: bool = False,
in_place: bool = False,
) -> RangedSummarizedExperiment:
Expand Down Expand Up @@ -742,9 +740,9 @@ def restrict(

def narrow(
self,
start: Optional[Union[int, List[int], np.ndarray]] = None,
width: Optional[Union[int, List[int], np.ndarray]] = None,
end: Optional[Union[int, List[int], np.ndarray]] = None,
start: int | list[int] | np.ndarray | None = None,
width: int | list[int] | np.ndarray | None = None,
end: int | list[int] | np.ndarray | None = None,
in_place: bool = False,
) -> RangedSummarizedExperiment:
"""Narrow genomic positions by provided ``start``, ``width`` and ``end`` parameters.
Expand Down
14 changes: 7 additions & 7 deletions src/summarizedexperiment/SummarizedExperiment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Any, Dict, List, Optional, Union
from typing import Any
from warnings import warn

import biocframe
Expand Down Expand Up @@ -30,12 +30,12 @@ class SummarizedExperiment(BaseSE):

def __init__(
self,
assays: Dict[str, Any] = None,
row_data: Optional[biocframe.BiocFrame] = None,
column_data: Optional[biocframe.BiocFrame] = None,
row_names: Optional[List[str]] = None,
column_names: Optional[List[str]] = None,
metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None,
assays: dict[str, Any] = None,
row_data: biocframe.BiocFrame | None = None,
column_data: biocframe.BiocFrame | None = None,
row_names: list[str] | None = None,
column_names: list[str] | None = None,
metadata: dict[str, Any] | ut.NamedList | None = None,
_validate: bool = True,
) -> None:
"""Initialize a Summarized Experiment (SE).
Expand Down
2 changes: 1 addition & 1 deletion src/summarizedexperiment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
finally:
del version, PackageNotFoundError

from .SummarizedExperiment import SummarizedExperiment
from .RangedSummarizedExperiment import RangedSummarizedExperiment
from .SummarizedExperiment import SummarizedExperiment
Loading
Loading