From c90461df2b75d1059acba542f27e066d12d2c255 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 8 Jul 2026 20:39:43 -0700 Subject: [PATCH 1/7] minor fix to slicing --- src/summarizedexperiment/base.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/summarizedexperiment/base.py b/src/summarizedexperiment/base.py index 3da43a5..be2f7a7 100644 --- a/src/summarizedexperiment/base.py +++ b/src/summarizedexperiment/base.py @@ -1125,7 +1125,7 @@ def __getitem__( Returns: Same type as caller with the sliced rows and columns. """ - if isinstance(args, (str, int)): + if not isinstance(args, tuple): return self.get_slice(args, slice(None)) if isinstance(args, tuple): @@ -1139,8 +1139,6 @@ def __getitem__( else: raise ValueError(f"`{type(self).__name__}` only supports 2-dimensional slicing.") - raise TypeError("args must be a sequence or a scalar integer or string or a tuple of atmost 2 values.") - ################################ ######>> AnnData interop <<##### ################################ From 30f284d20a594884bcbb8a2d332ac9d3293cbbbd Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 8 Jul 2026 20:40:34 -0700 Subject: [PATCH 2/7] slice RSE by granges overlaps --- .../RangedSummarizedExperiment.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/summarizedexperiment/RangedSummarizedExperiment.py b/src/summarizedexperiment/RangedSummarizedExperiment.py index abb57cf..3414ea5 100644 --- a/src/summarizedexperiment/RangedSummarizedExperiment.py +++ b/src/summarizedexperiment/RangedSummarizedExperiment.py @@ -392,6 +392,17 @@ def seq_info(self) -> SeqInfo: # rest of them are inherited from BaseSE. + def _normalize_row_slice(self, rows: Union[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"): + 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]], From 7b1a5db29c9dc19c9eb55662a2a689b199820c17 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 8 Jul 2026 20:41:38 -0700 Subject: [PATCH 3/7] test slice rse by granges --- tests/test_rse_genomic_slice.py | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/test_rse_genomic_slice.py diff --git a/tests/test_rse_genomic_slice.py b/tests/test_rse_genomic_slice.py new file mode 100644 index 0000000..c12f673 --- /dev/null +++ b/tests/test_rse_genomic_slice.py @@ -0,0 +1,45 @@ +import pytest +import numpy as np +import pandas as pd +import genomicranges +from summarizedexperiment.RangedSummarizedExperiment import RangedSummarizedExperiment + +__author__ = "jkanche" +__copyright__ = "jkanche" +__license__ = "MIT" + + +def test_RSE_genomic_slice(): + df_gr = pd.DataFrame( + { + "seqnames": ["chr1", "chr2", "chr3"], + "starts": [100, 200, 300], + "ends": [150, 250, 350], + "strand": ["+", "-", "*"], + } + ) + gr = genomicranges.GenomicRanges.from_pandas(df_gr) + + rse = RangedSummarizedExperiment( + assays={"counts": np.random.rand(3, 2)}, + row_ranges=gr, + ) + + query_df = pd.DataFrame( + { + "seqnames": ["chr2"], + "starts": [220], + "ends": [230], + "strand": ["-"], + } + ) + query_gr = genomicranges.GenomicRanges.from_pandas(query_df) + + sliced = rse[query_gr, :] + assert sliced is not None + assert sliced.shape == (1, 2) + assert sliced.row_ranges.get_seqnames() == ["chr2"] + + sliced_1d = rse[query_gr] + assert sliced_1d.shape == (1, 2) + assert sliced_1d.row_ranges.get_seqnames() == ["chr2"] From 693ced1eb5e93ae2a6d7932904dc8d238ea94e77 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 8 Jul 2026 20:42:20 -0700 Subject: [PATCH 4/7] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3754935..0ba8bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Version 0.7.0 + +- Extended `__getitem__` subscripting on `RangedSummarizedExperiment` to support direct slicing by `GenomicRanges` or `CompressedGenomicRangesList` objects. + ## Version 0.6.0 - 0.6.5 - Classes now extend `BiocObject` from biocutils, which provides a metadata field. From a5163c6b2ae860dbd02e55318a174ba109109c03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 03:43:30 +0000 Subject: [PATCH 5/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_rse_genomic_slice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_rse_genomic_slice.py b/tests/test_rse_genomic_slice.py index c12f673..c0bb130 100644 --- a/tests/test_rse_genomic_slice.py +++ b/tests/test_rse_genomic_slice.py @@ -19,12 +19,12 @@ def test_RSE_genomic_slice(): } ) gr = genomicranges.GenomicRanges.from_pandas(df_gr) - + rse = RangedSummarizedExperiment( assays={"counts": np.random.rand(3, 2)}, row_ranges=gr, ) - + query_df = pd.DataFrame( { "seqnames": ["chr2"], @@ -34,12 +34,12 @@ def test_RSE_genomic_slice(): } ) query_gr = genomicranges.GenomicRanges.from_pandas(query_df) - + sliced = rse[query_gr, :] assert sliced is not None assert sliced.shape == (1, 2) assert sliced.row_ranges.get_seqnames() == ["chr2"] - + sliced_1d = rse[query_gr] assert sliced_1d.shape == (1, 2) assert sliced_1d.row_ranges.get_seqnames() == ["chr2"] From 99a15bb391f3d92d8bde49258ee2022083c94822 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 8 Jul 2026 21:04:27 -0700 Subject: [PATCH 6/7] improve test coverage --- src/summarizedexperiment/base.py | 9 +- tests/test_RSE_methods.py | 158 +++++++++++++++++++------------ tests/test_SE_validation.py | 128 +++++++++++++++++++++++++ tests/test_rse_genomic_slice.py | 45 --------- 4 files changed, 232 insertions(+), 108 deletions(-) create mode 100644 tests/test_SE_validation.py delete mode 100644 tests/test_rse_genomic_slice.py diff --git a/src/summarizedexperiment/base.py b/src/summarizedexperiment/base.py index be2f7a7..fcb2e34 100644 --- a/src/summarizedexperiment/base.py +++ b/src/summarizedexperiment/base.py @@ -33,7 +33,10 @@ def _guess_assay_shape(assays, rows, cols, row_names, col_names) -> tuple: _keys = list(assays.keys()) if len(_keys) > 0: _first = _keys[0] - return assays[_first].shape + mat = assays[_first] + if not hasattr(mat, "shape"): + raise TypeError(f"Assay: '{_first}' is not a supported matrix representation.") + return mat.shape _r = 0 if rows is not None: @@ -201,14 +204,14 @@ def __init__( self._cols = _sanitize_frame(column_data, self._shape[1]) if row_names is None: - row_names = self._rows.row_names + row_names = self._rows.row_names if hasattr(self._rows, "row_names") else None if row_names is not None and not isinstance(row_names, ut.Names): row_names = ut.Names(row_names) self._row_names = row_names if column_names is None: - column_names = self._cols.row_names + column_names = self._cols.row_names if hasattr(self._cols, "row_names") else None if column_names is not None and not isinstance(column_names, ut.Names): column_names = ut.Names(column_names) diff --git a/tests/test_RSE_methods.py b/tests/test_RSE_methods.py index 9f40a87..0b788a3 100644 --- a/tests/test_RSE_methods.py +++ b/tests/test_RSE_methods.py @@ -4,6 +4,8 @@ import numpy as np import pandas as pd import pytest +from genomicranges import GenomicRanges, IRanges + from summarizedexperiment.RangedSummarizedExperiment import RangedSummarizedExperiment from summarizedexperiment.SummarizedExperiment import SummarizedExperiment @@ -15,42 +17,36 @@ nrows = 200 ncols = 6 counts = np.random.rand(nrows, ncols) -df_gr = pd.DataFrame( - { - "seqnames": [ - "chr1", - "chr2", - "chr2", - "chr2", - "chr1", - "chr1", - "chr3", - "chr3", - "chr3", - "chr3", - ] - * 20, - "starts": range(100, 300), - "ends": range(110, 310), - "strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"] * 20, - "score": range(0, 200), - "GC": [random() for _ in range(10)] * 20, - } -) +df_gr = pd.DataFrame({ + "seqnames": [ + "chr1", + "chr2", + "chr2", + "chr2", + "chr1", + "chr1", + "chr3", + "chr3", + "chr3", + "chr3", + ] + * 20, + "starts": range(100, 300), + "ends": range(110, 310), + "strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"] * 20, + "score": range(0, 200), + "GC": [random() for _ in range(10)] * 20, +}) gr = genomicranges.GenomicRanges.from_pandas(df_gr) -col_data = pd.DataFrame( - { - "treatment": ["ChIP", "Input"] * 3, - } -) +col_data = pd.DataFrame({ + "treatment": ["ChIP", "Input"] * 3, +}) def test_RSE_props(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -78,9 +74,7 @@ def test_RSE_props(): def test_RSE_subset(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -96,9 +90,7 @@ def test_RSE_subset(): def test_RSE_subset_assays(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -112,9 +104,7 @@ def test_RSE_subset_assays(): def test_RSE_coverage(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -125,9 +115,7 @@ def test_RSE_coverage(): def test_RSE_distance_methods(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -143,9 +131,7 @@ def test_RSE_distance_methods(): def test_RSE_range_methods(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -170,9 +156,7 @@ def test_RSE_range_methods(): def test_RSE_search(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -185,9 +169,7 @@ def test_RSE_search(): def test_RSE_sort_order(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -200,10 +182,9 @@ def test_RSE_sort_order(): assert res is not None assert len(res.row_ranges) == len(tse.row_ranges) + def test_RSE_assay_getters_and_setters(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) assert tse is not None assert isinstance(tse, RangedSummarizedExperiment) @@ -221,10 +202,9 @@ def test_RSE_assay_getters_and_setters(): assert tse.get_assay("new_counts") is not None assert new_tse.get_assay("new_counts") is not None + def test_RSE_to_se(): - rse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + rse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) se = rse.to_summarizedexperiment() @@ -232,10 +212,9 @@ def test_RSE_to_se(): assert isinstance(se, SummarizedExperiment) assert rse.shape == se.shape + def test_RSE_to_se_with_ranges(): - tse = RangedSummarizedExperiment( - assays={"counts": counts}, row_ranges=gr, column_data=col_data - ) + tse = RangedSummarizedExperiment(assays={"counts": counts}, row_ranges=gr, column_data=col_data) se = tse.to_summarizedexperiment() @@ -247,3 +226,62 @@ def test_RSE_to_se_with_ranges(): assert "seqnames" in se.row_data.column_names assert "starts" in se.row_data.column_names assert "ends" in se.row_data.column_names + + +def test_RSE_combine(): + gr1 = GenomicRanges(seqnames=["chr1", "chr2"], ranges=IRanges([1, 100], [10, 50]), strand=["+", "-"]) + rse1 = RangedSummarizedExperiment( + assays={"counts": np.array([[1, 2], [3, 4]])}, row_ranges=gr1, column_names=["sample1", "sample2"] + ) + + gr2 = GenomicRanges(seqnames=["chr3"], ranges=IRanges([200], [20]), strand=["*"]) + rse2 = RangedSummarizedExperiment( + assays={"counts": np.array([[5, 6]])}, row_ranges=gr2, column_names=["sample1", "sample2"] + ) + + combined_rows = rse1.combine_rows(rse2) + assert combined_rows.shape == (3, 2) + assert list(combined_rows.row_ranges.get_seqnames()) == ["chr1", "chr2", "chr3"] + + combined_rows_rel = rse1.relaxed_combine_rows(rse2) + assert combined_rows_rel.shape == (3, 2) + + rse3 = RangedSummarizedExperiment(assays={"counts": np.array([[7], [8]])}, row_ranges=gr1, column_names=["sample3"]) + combined_cols = rse1.combine_columns(rse3) + assert combined_cols.shape == (2, 3) + assert list(combined_cols.column_names) == ["sample1", "sample2", "sample3"] + + combined_cols_rel = rse1.relaxed_combine_columns(rse3) + assert combined_cols_rel.shape == (2, 3) + + +def test_RSE_genomic_slice(): + df_gr = pd.DataFrame({ + "seqnames": ["chr1", "chr2", "chr3"], + "starts": [100, 200, 300], + "ends": [150, 250, 350], + "strand": ["+", "-", "*"], + }) + gr = genomicranges.GenomicRanges.from_pandas(df_gr) + + rse = RangedSummarizedExperiment( + assays={"counts": np.random.rand(3, 2)}, + row_ranges=gr, + ) + + query_df = pd.DataFrame({ + "seqnames": ["chr2"], + "starts": [220], + "ends": [230], + "strand": ["-"], + }) + query_gr = genomicranges.GenomicRanges.from_pandas(query_df) + + sliced = rse[query_gr, :] + assert sliced is not None + assert sliced.shape == (1, 2) + assert sliced.row_ranges.get_seqnames() == ["chr2"] + + sliced_1d = rse[query_gr] + assert sliced_1d.shape == (1, 2) + assert sliced_1d.row_ranges.get_seqnames() == ["chr2"] diff --git a/tests/test_SE_validation.py b/tests/test_SE_validation.py new file mode 100644 index 0000000..5f380c0 --- /dev/null +++ b/tests/test_SE_validation.py @@ -0,0 +1,128 @@ +from copy import deepcopy + +import numpy as np +import pytest +from biocframe import BiocFrame +from genomicranges import GenomicRanges +from iranges import IRanges + +from summarizedexperiment import RangedSummarizedExperiment, SummarizedExperiment + +__author__ = "jkanche" +__copyright__ = "jkanche" +__license__ = "MIT" + + +def test_assays_validation(): + with pytest.raises(Exception): + SummarizedExperiment(assays=123) + + with pytest.raises(TypeError): + SummarizedExperiment(assays={"counts": "invalid"}) + + with pytest.raises(ValueError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2, 2)}) + + with pytest.raises(ValueError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 3), "log": np.random.rand(2, 4)}) + + +def test_rows_cols_validation(): + with pytest.raises(TypeError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2)}, row_data="invalid") + + with pytest.raises(ValueError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2)}, row_data=BiocFrame({}, number_of_rows=3)) + + with pytest.raises(ValueError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2)}, row_names=["a", "b", "c"]) + + with pytest.raises(TypeError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2)}, column_data="invalid") + + with pytest.raises(ValueError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2)}, column_data=BiocFrame({}, number_of_rows=3)) + + with pytest.raises(ValueError): + SummarizedExperiment(assays={"counts": np.random.rand(2, 2)}, column_names=["a", "b", "c"]) + + +def test_deepcopy(): + se = SummarizedExperiment( + assays={"counts": np.random.rand(2, 2)}, + row_names=["gene1", "gene2"], + column_names=["sample1", "sample2"], + metadata={"study": "cancer"}, + ) + + se_copy = deepcopy(se) + assert se_copy is not se + assert se_copy.shape == se.shape + assert list(se_copy.row_names) == list(se.row_names) + assert list(se_copy.column_names) == list(se.column_names) + assert se_copy.metadata["study"] == "cancer" + + gr = GenomicRanges(seqnames=["chr1", "chr2"], ranges=IRanges([1, 100], [10, 50]), strand=["+", "-"]) + rse = RangedSummarizedExperiment( + assays={"counts": np.random.rand(2, 2)}, + row_ranges=gr, + column_names=["sample1", "sample2"], + metadata={"study": "cancer"}, + ) + rse_copy = deepcopy(rse) + assert rse_copy is not rse + assert rse_copy.shape == rse.shape + assert len(rse_copy.row_ranges) == len(rse.row_ranges) + + +def test_properties_get_set(): + se = SummarizedExperiment( + assays={"counts": np.random.rand(2, 2)}, row_names=["gene1", "gene2"], column_names=["sample1", "sample2"] + ) + + se.assays = {"counts2": np.random.rand(2, 2)} + assert "counts2" in se.assay_names + + row_df = BiocFrame({"feat": [1, 2]}) + se.row_data = row_df + assert "feat" in se.row_data.colnames + se.rowdata = row_df + assert "feat" in se.rowdata.colnames + + col_df = BiocFrame({"sample_id": [1, 2]}) + se.col_data = col_df + assert "sample_id" in se.col_data.colnames + se.coldata = col_df + assert "sample_id" in se.coldata.colnames + se.columndata = col_df + assert "sample_id" in se.columndata.colnames + + rdata = se.get_row_data(replace_row_names=False) + cdata = se.get_column_data(replace_row_names=False) + assert rdata.row_names is None + assert cdata.row_names is None + + new_r = BiocFrame({"feat": [3, 4]}, row_names=["g1", "g2"]) + se2 = se.set_row_data(new_r, replace_row_names=True) + assert list(se2.row_names) == ["g1", "g2"] + + new_c = BiocFrame({"sample_id": [3, 4]}, row_names=["s1", "s2"]) + se3 = se.set_column_data(new_c, replace_column_names=True) + assert list(se3.column_names) == ["s1", "s2"] + + +def test_hasattr_overlaps_slice(): + import unittest.mock as mock + + gr = GenomicRanges(seqnames=["chr1", "chr2"], ranges=IRanges([1, 100], [10, 50]), strand=["+", "-"]) + rse = RangedSummarizedExperiment( + assays={"counts": np.random.rand(2, 2)}, row_ranges=gr, column_names=["sample1", "sample2"] + ) + + class OverlapMock: + def find_overlaps(self, query): + pass + + with mock.patch.object(rse.row_ranges, "find_overlaps", return_value=BiocFrame({"self_hits": [0]})): + res = rse[OverlapMock(), :] + assert res.shape == (1, 2) diff --git a/tests/test_rse_genomic_slice.py b/tests/test_rse_genomic_slice.py deleted file mode 100644 index c12f673..0000000 --- a/tests/test_rse_genomic_slice.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest -import numpy as np -import pandas as pd -import genomicranges -from summarizedexperiment.RangedSummarizedExperiment import RangedSummarizedExperiment - -__author__ = "jkanche" -__copyright__ = "jkanche" -__license__ = "MIT" - - -def test_RSE_genomic_slice(): - df_gr = pd.DataFrame( - { - "seqnames": ["chr1", "chr2", "chr3"], - "starts": [100, 200, 300], - "ends": [150, 250, 350], - "strand": ["+", "-", "*"], - } - ) - gr = genomicranges.GenomicRanges.from_pandas(df_gr) - - rse = RangedSummarizedExperiment( - assays={"counts": np.random.rand(3, 2)}, - row_ranges=gr, - ) - - query_df = pd.DataFrame( - { - "seqnames": ["chr2"], - "starts": [220], - "ends": [230], - "strand": ["-"], - } - ) - query_gr = genomicranges.GenomicRanges.from_pandas(query_df) - - sliced = rse[query_gr, :] - assert sliced is not None - assert sliced.shape == (1, 2) - assert sliced.row_ranges.get_seqnames() == ["chr2"] - - sliced_1d = rse[query_gr] - assert sliced_1d.shape == (1, 2) - assert sliced_1d.row_ranges.get_seqnames() == ["chr2"] From 034ebb8fa9583244c7b7e3bfe2bd2572a92b888a Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 8 Jul 2026 21:08:39 -0700 Subject: [PATCH 7/7] fix iranges import in test --- tests/test_RSE_methods.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_RSE_methods.py b/tests/test_RSE_methods.py index 0b788a3..6c168c8 100644 --- a/tests/test_RSE_methods.py +++ b/tests/test_RSE_methods.py @@ -4,7 +4,8 @@ import numpy as np import pandas as pd import pytest -from genomicranges import GenomicRanges, IRanges +from genomicranges import GenomicRanges +from iranges import IRanges from summarizedexperiment.RangedSummarizedExperiment import RangedSummarizedExperiment from summarizedexperiment.SummarizedExperiment import SummarizedExperiment