From 5543a8138674e4665f149c7955c30462647c93b7 Mon Sep 17 00:00:00 2001 From: OfficialBishal Date: Thu, 25 Jun 2026 07:55:07 -0400 Subject: [PATCH] fix: assign_view drop_unassigned no longer drops rows with NaNs in other columns assign_view(drop_unassigned=True) dropped every row containing a NaN in any column, so intervals that were assigned to a view region were lost if they had a NaN in an unrelated column (e.g. cooler bins with missing weights). Only drop intervals whose view assignment is NaN, and add a regression test. Closes #160 --- CHANGES.md | 5 +++++ src/bioframe/ops.py | 4 +++- tests/test_ops.py | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 5c466d6d..91c6ffb7 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: + +- `assign_view(..., drop_unassigned=True)` now drops only intervals that fall outside the view, + instead of also dropping rows that contain NaNs in unrelated columns. + ## v0.8.0 Date: 2025-04-08 diff --git a/src/bioframe/ops.py b/src/bioframe/ops.py index 934603d8..82cab563 100644 --- a/src/bioframe/ops.py +++ b/src/bioframe/ops.py @@ -1893,7 +1893,9 @@ def assign_view( out_df.rename(columns={view_name_col + "_view": df_view_col}, inplace=True) if drop_unassigned: - out_df = out_df.iloc[pd.isna(out_df).any(axis=1).values == 0, :] + # only drop intervals that weren't assigned to a view region, not rows + # that happen to contain NaNs in other columns + out_df = out_df[pd.notna(out_df[df_view_col]).values] out_df.reset_index(inplace=True, drop=True) return_cols = [*list(df.columns), df_view_col] diff --git a/tests/test_ops.py b/tests/test_ops.py index 66c0fd2a..f0f662d3 100644 --- a/tests/test_ops.py +++ b/tests/test_ops.py @@ -2289,3 +2289,24 @@ def test_sort_bedframe(): assert ( df.dtypes == bioframe.sort_bedframe(df, view_df, view_name_col="fruit").dtypes ).all() + + +def test_assign_view_drop_unassigned_keeps_nan_in_other_columns(): + # drop_unassigned should only drop intervals that fall outside the view, not + # intervals that merely contain NaNs in unrelated columns (e.g. cooler bins with + # missing weights). See open2c/bioframe#160. + view_df = bioframe.make_viewframe([("chr1", 0, 100, "region1")]) + df = pd.DataFrame( + { + "chrom": ["chr1", "chr1", "chr2"], + "start": [10, 30, 10], + "end": [20, 40, 20], + "value": [np.nan, 5.0, 7.0], + } + ) + result = bioframe.assign_view(df, view_df, drop_unassigned=True) + + # chr2:10-20 is outside the view and dropped; chr1:10-20 is assigned and kept + # even though its `value` is NaN. + assert list(zip(result["chrom"], result["start"])) == [("chr1", 10), ("chr1", 30)] + assert pd.isna(result.loc[0, "value"])