From 32cce2e5b1b1cd984870478a720e44f7a3b563f4 Mon Sep 17 00:00:00 2001 From: Derrick Chambers Date: Tue, 4 Aug 2026 21:16:29 +0200 Subject: [PATCH] Restore review fixes dropped from #808 and #810 (#814) --- dascore/utils/pd.py | 4 +++- docs/tutorial/coords.qmd | 2 +- tests/test_utils/test_pd.py | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dascore/utils/pd.py b/dascore/utils/pd.py index 05e1ad9e1..07238f4e6 100644 --- a/dascore/utils/pd.py +++ b/dascore/utils/pd.py @@ -148,7 +148,9 @@ def _check_misdirected_range_query(key, val, df): base = key[:-4] if key.endswith(("_min", "_max")) else None if base is None or not {f"{base}_min", f"{base}_max"}.issubset(set(df.columns)): return - if not any(x is ... or x is None for x in val): + # Only a two element sequence can be a range. Anything else is a + # membership check, where None may be a legitimate value to match. + if len(val) != 2 or not any(x is ... or x is None for x in val): return msg = ( f"An open bound (... or None) is not valid in the query for column " diff --git a/docs/tutorial/coords.qmd b/docs/tutorial/coords.qmd index f5d9b7c03..e0c712bbc 100644 --- a/docs/tutorial/coords.qmd +++ b/docs/tutorial/coords.qmd @@ -122,7 +122,7 @@ sorted_data = data[indexer, :] ### Snap [`snap`](`dascore.core.coords.BaseCoord.snap`) is used to calculate an average spacing between samples and "snap" all values to that spacing. If the coordinate is not sorted, it will be sorted in the process. This method should be used with care since it causes some loss in precision and can introduce inaccuracies in down-stream calculations. The min and max of the coordinate remain unchanged. -Unlike [`sort`](`dascore.core.coords.BaseCoord.sort`), `snap` returns only a new coordinate, not an indexer. Since snapping an unsorted coordinate also reorders it, use [`CoordManager.snap`](`dascore.core.coordmanager.CoordManager.snap`) instead when an associated data array needs to stay aligned. +Unlike [`sort`](`dascore.core.coords.BaseCoord.sort`), `snap` returns only a new coordinate and no indexer, because it replaces the coordinate values rather than permuting them. This means that for an unsorted coordinate the snapped values no longer label the samples they used to. Use [`CoordManager.snap`](`dascore.core.coordmanager.CoordManager.snap`), which sorts the coordinate and its associated array together before snapping, when data alignment matters. ```{python} import numpy as np diff --git a/tests/test_utils/test_pd.py b/tests/test_utils/test_pd.py index 4e792c5d8..4eba546f7 100644 --- a/tests/test_utils/test_pd.py +++ b/tests/test_utils/test_pd.py @@ -272,6 +272,15 @@ def test_closed_range_on_interval_column_still_isin(self, example_df_2): out = filter_df(example_df_2, bp_min=tuple(vals)) assert np.all(out == example_df_2["bp_min"].isin(vals)) + @pytest.mark.parametrize("val", [[None], [100, None, 125]]) + def test_non_range_shaped_collection_still_isin(self, example_df_2, val): + """ + Only a two element sequence can be a range, so other collections stay + membership checks even when they contain None. + """ + out = filter_df(example_df_2, bp_min=val) + assert np.all(out == example_df_2["bp_min"].isin(val)) + def test_ellipsis_kept_for_non_interval_column(self, example_df_2): """ Columns with no min/max pair are plain isin checks; an ellipsis there