From e86cc6667b9c89acaf77e1b4502aab85c2c6722a Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Fri, 21 Aug 2026 08:53:37 -0700 Subject: [PATCH 1/5] Fix the to_pandas method when names are available causing mismatch in index --- CHANGELOG.md | 3 ++- src/genomicranges/GenomicRanges.py | 6 ++--- tests/test_gr_basic.py | 37 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc62e52..b3e6b68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ # Changelog -## Version 0.8.0 - 0.8.4 +## Version 0.8.0 - 0.8.5 - Rename `GenomicRangesList` to `CompressedGenomicRangesList` and now extends compressed-lists. - Classes extend `BiocObject` from biocutils, provides a default metadata attribute and helper functions. - Rename `validate` to `_validate` for consistency with the rest of the packages and classes. - Fix for empty compressed genomic ranges list. - Aliases `GRanges` and `CompressedGRangesList`. +- Fixed a bug in `to_pandas()` when `names` and `mcols` are present. ## Version 0.7.0 - 0.7.3 diff --git a/src/genomicranges/GenomicRanges.py b/src/genomicranges/GenomicRanges.py index 17a1373..04a7898 100644 --- a/src/genomicranges/GenomicRanges.py +++ b/src/genomicranges/GenomicRanges.py @@ -1006,13 +1006,13 @@ def to_pandas(self): _rdf["seqnames"] = self.get_seqnames() _rdf["strand"] = self.get_strand(as_type="list") - if self._names is not None: - _rdf.index = self._names - if self._mcols is not None: if self._mcols.shape[1] > 0: _rdf = pd.concat([_rdf, self._mcols.to_pandas()], axis=1) + if self._names is not None: + _rdf.index = self._names + return _rdf @classmethod diff --git a/tests/test_gr_basic.py b/tests/test_gr_basic.py index 1e905e8..7fc9263 100644 --- a/tests/test_gr_basic.py +++ b/tests/test_gr_basic.py @@ -113,6 +113,43 @@ def test_export_pandas_with_mcols(): assert isinstance(df, pd.DataFrame) +def test_export_pandas_with_names_and_mcols(): + from biocframe import BiocFrame + from iranges import IRanges + + ranges = IRanges(start=[0, 10, 20], width=[5, 5, 5]) + mcols = BiocFrame({"gene_id": ["g1", "g2", "g3"], "gene_name": ["A", "B", "C"]}) + gr_named = GenomicRanges( + seqnames=["1", "1", "1"], + ranges=ranges, + strand=["+", "+", "-"], + names=["g1", "g2", "g3"], + mcols=mcols, + ) + df = gr_named.to_pandas() + assert df is not None + assert df.shape == (3, 7) + assert df.index.tolist() == ["g1", "g2", "g3"] + +def test_export_polars_with_names_and_mcols(): + from biocframe import BiocFrame + from iranges import IRanges + import polars as pl + + ranges = IRanges(start=[0, 10, 20], width=[5, 5, 5]) + mcols = BiocFrame({"gene_id": ["g1", "g2", "g3"], "gene_name": ["A", "B", "C"]}) + gr_named = GenomicRanges( + seqnames=["1", "1", "1"], + ranges=ranges, + strand=["+", "+", "-"], + names=["g1", "g2", "g3"], + mcols=mcols, + ) + df = gr_named.to_polars() + assert df is not None + assert df.shape == (3, 8) # 8 because polars adds a 'rownames' column + assert df["rownames"].to_list() == ["g1", "g2", "g3"] + def test_combine(): g_src = GenomicRanges( seqnames=["chr1", "chr2", "chr1", "chr3", "chr2"], From 1b758d35a02230c63f004582e91f90f138ae6e9c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 15:54:27 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_gr_basic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_gr_basic.py b/tests/test_gr_basic.py index 7fc9263..57f2ef6 100644 --- a/tests/test_gr_basic.py +++ b/tests/test_gr_basic.py @@ -131,10 +131,10 @@ def test_export_pandas_with_names_and_mcols(): assert df.shape == (3, 7) assert df.index.tolist() == ["g1", "g2", "g3"] + def test_export_polars_with_names_and_mcols(): from biocframe import BiocFrame from iranges import IRanges - import polars as pl ranges = IRanges(start=[0, 10, 20], width=[5, 5, 5]) mcols = BiocFrame({"gene_id": ["g1", "g2", "g3"], "gene_name": ["A", "B", "C"]}) @@ -147,9 +147,10 @@ def test_export_polars_with_names_and_mcols(): ) df = gr_named.to_polars() assert df is not None - assert df.shape == (3, 8) # 8 because polars adds a 'rownames' column + assert df.shape == (3, 8) # 8 because polars adds a 'rownames' column assert df["rownames"].to_list() == ["g1", "g2", "g3"] + def test_combine(): g_src = GenomicRanges( seqnames=["chr1", "chr2", "chr1", "chr3", "chr2"], From 10470348a3dc50d1a2a1c6119695c34b929ebf15 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Fri, 21 Aug 2026 09:03:37 -0700 Subject: [PATCH 3/5] coece to list before adding names to index --- src/genomicranges/GenomicRanges.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/genomicranges/GenomicRanges.py b/src/genomicranges/GenomicRanges.py index 04a7898..e0e8d3a 100644 --- a/src/genomicranges/GenomicRanges.py +++ b/src/genomicranges/GenomicRanges.py @@ -1011,7 +1011,7 @@ def to_pandas(self): _rdf = pd.concat([_rdf, self._mcols.to_pandas()], axis=1) if self._names is not None: - _rdf.index = self._names + _rdf.index = list(self._names) return _rdf @@ -1088,7 +1088,7 @@ def to_polars(self): _rdf = _rdf.with_columns(seqnames=self.get_seqnames(), strand=self.get_strand(as_type="list")) if self._names is not None: - _rdf = _rdf.with_columns(rownames=self._names) + _rdf = _rdf.with_columns(rownames=list(self._names)) if self._mcols is not None: if self._mcols.shape[1] > 0: From 84bfa93c6ab1d960d70e53e4c865d7f0e21508d2 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Fri, 21 Aug 2026 09:22:31 -0700 Subject: [PATCH 4/5] coerce to pl.series before assigning to a polars dataframe --- src/genomicranges/GenomicRanges.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/genomicranges/GenomicRanges.py b/src/genomicranges/GenomicRanges.py index e0e8d3a..a911a37 100644 --- a/src/genomicranges/GenomicRanges.py +++ b/src/genomicranges/GenomicRanges.py @@ -1085,10 +1085,13 @@ def to_polars(self): import polars as pl _rdf = self._ranges.to_polars() - _rdf = _rdf.with_columns(seqnames=self.get_seqnames(), strand=self.get_strand(as_type="list")) + _rdf = _rdf.with_columns( + seqnames=pl.Series(self.get_seqnames()), + strand=pl.Series(self.get_strand(as_type="list")) + ) if self._names is not None: - _rdf = _rdf.with_columns(rownames=list(self._names)) + _rdf = _rdf.with_columns(rownames=pl.Series(list(self._names))) if self._mcols is not None: if self._mcols.shape[1] > 0: From 92596cbf8edb4477900014c442a54155b22902cc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:22:41 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/genomicranges/GenomicRanges.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/genomicranges/GenomicRanges.py b/src/genomicranges/GenomicRanges.py index a911a37..acb1ae6 100644 --- a/src/genomicranges/GenomicRanges.py +++ b/src/genomicranges/GenomicRanges.py @@ -1086,8 +1086,7 @@ def to_polars(self): _rdf = self._ranges.to_polars() _rdf = _rdf.with_columns( - seqnames=pl.Series(self.get_seqnames()), - strand=pl.Series(self.get_strand(as_type="list")) + seqnames=pl.Series(self.get_seqnames()), strand=pl.Series(self.get_strand(as_type="list")) ) if self._names is not None: