From 5f31d1da4b6b53ace928557d1275b6da3a7364a0 Mon Sep 17 00:00:00 2001 From: OfficialBishal Date: Thu, 25 Jun 2026 03:07:16 -0400 Subject: [PATCH] fix: restore one-sided return_input in overlap overlap() historically accepted 1/"1"/"left" and 2/"2"/"right" to return only one side's columns, but a refactor reduced return_input to a plain truthiness check, so every non-False value returned both sides. Restore the documented values, raise ValueError on unsupported input, and add tests. Closes #197 --- CHANGES.md | 5 +++++ src/bioframe/ops.py | 25 +++++++++++++++++++++---- tests/test_ops.py | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5c466d6d..26ee7bee 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,11 @@ ## [Upcoming release](https://github.com/open2c/bioframe/compare/v0.8.0...HEAD) +Bug fixes: + +- `overlap` again honors `return_input=1`/`"left"` and `return_input=2`/`"right"` to return only + one side's columns; unsupported values now raise `ValueError` instead of silently returning both. + ## v0.8.0 Date: 2025-04-08 diff --git a/src/bioframe/ops.py b/src/bioframe/ops.py index 934603d8..da5f02f2 100644 --- a/src/bioframe/ops.py +++ b/src/bioframe/ops.py @@ -387,8 +387,11 @@ def overlap( outer: use the union of the set of intervals from df1 and df2 inner: use intersection of the set of intervals from df1 and df2 - return_input : bool, optional - If True, return columns from input dfs. Default True. + return_input : bool, int, or str, optional + Controls which input columns are returned. If True (default), return + columns from both input dataframes; if False, return neither. To return + only one side, pass ``1``, ``"1"``, or ``"left"`` for ``df1``, or ``2``, + ``"2"``, or ``"right"`` for ``df2``. return_index : bool, optional If True, return indicies of overlapping pairs as two new columns @@ -461,6 +464,20 @@ def overlap( _verify_columns(df1, on) _verify_columns(df2, on) + if return_input is True: + return_left = return_right = True + elif return_input is False: + return_left = return_right = False + elif return_input in (1, "1", "left"): + return_left, return_right = True, False + elif return_input in (2, "2", "right"): + return_left, return_right = False, True + else: + raise ValueError( + "return_input must be one of True, False, 1, 2, 'left', 'right'; " + f"got {return_input!r}" + ) + events1, events2 = _overlap_intidxs( df1, df2, @@ -499,11 +516,11 @@ def overlap( df_input_1 = None df_input_2 = None - if return_input: + if return_left: df_input_1 = df1.iloc[events1].reset_index(drop=True) df_input_1.columns = [c + suffixes[0] for c in df_input_1.columns] - if return_input: + if return_right: df_input_2 = df2.iloc[events2].reset_index(drop=True) df_input_2.columns = [c + suffixes[1] for c in df_input_2.columns] diff --git a/tests/test_ops.py b/tests/test_ops.py index 66c0fd2a..c9541d29 100644 --- a/tests/test_ops.py +++ b/tests/test_ops.py @@ -772,6 +772,46 @@ def test_overlap(): ) +def test_overlap_return_input(): + df1 = pd.DataFrame( + [["chr1", 3, 8], ["chr1", 12, 16]], + columns=["chrom", "start", "end"], + ) + df2 = pd.DataFrame( + [["chr1", 5, 10]], + columns=["chrom", "start", "end"], + ) + left_cols = ["chrom", "start", "end"] + right_cols = ["chrom_", "start_", "end_"] + + # True / False return both / neither sides + assert ( + list(bioframe.overlap(df1, df2, how="inner", return_input=True).columns) + == left_cols + right_cols + ) + assert ( + list(bioframe.overlap(df1, df2, how="inner", return_input=False).columns) == [] + ) + + # 1 / "1" / "left" return only df1 columns + for val in (1, "1", "left"): + assert ( + list(bioframe.overlap(df1, df2, how="inner", return_input=val).columns) + == left_cols + ) + + # 2 / "2" / "right" return only df2 columns + for val in (2, "2", "right"): + assert ( + list(bioframe.overlap(df1, df2, how="inner", return_input=val).columns) + == right_cols + ) + + # values outside the supported set raise instead of silently returning both + with pytest.raises(ValueError): + bioframe.overlap(df1, df2, how="inner", return_input="both") + + def test_overlap_preserves_coord_dtypes(): df1 = pd.DataFrame( [