From e4a9504f8d96955b39e1eb7ef0d7de7c8fbd1a5f Mon Sep 17 00:00:00 2001 From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:24:34 +0530 Subject: [PATCH 1/2] fix: support non-float polars columns in is_nan_or_none --- tests/test_processing.py | 11 +++++++++++ utilsforecast/processing.py | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/tests/test_processing.py b/tests/test_processing.py index fbe94ab..3cc22a2 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -117,6 +117,17 @@ def test_is_nan_or_none(): is_nan_or_none(pl.Series([np.nan, 1.0, None])).to_numpy(), np.array([True, False, True]), ) + for values in (["a", None, "b"], [True, None, False], [1, None, 2]): + np.testing.assert_equal( + is_nan_or_none(pl.Series(values)).to_numpy(), + np.array([False, True, False]), + ) + np.testing.assert_equal( + is_nan_or_none( + pl.Series(["a", None, "b"], dtype=pl.Categorical) + ).to_numpy(), + np.array([False, True, False]), + ) def test_vertical_concat_pd(): diff --git a/utilsforecast/processing.py b/utilsforecast/processing.py index c6bf34d..75b38ba 100644 --- a/utilsforecast/processing.py +++ b/utilsforecast/processing.py @@ -247,6 +247,10 @@ def is_none(s: Series) -> Series: def is_nan_or_none(s: Series) -> Series: + if isinstance(s, pl_Series) and not s.dtype.is_float(): + # polars only supports is_nan on numeric columns and only floats + # can hold NaN, so other columns (e.g. strings) can only have nulls + return s.is_null() return is_nan(s) | is_none(s) From a4c1d452a2e878ef3ca6899f7da0d5550f8026d7 Mon Sep 17 00:00:00 2001 From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com> Date: Sat, 10 Oct 2026 10:33:14 +0530 Subject: [PATCH 2/2] move the non-float polars handling to is_nan --- tests/test_processing.py | 25 ++++++++++++++----------- utilsforecast/processing.py | 9 ++++----- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/test_processing.py b/tests/test_processing.py index 3cc22a2..77b2aaa 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -93,6 +93,20 @@ def test_is_nan(): is_nan(pl.Series([np.nan, 1.0, None])).to_numpy(), np.array([True, False, None]), ) + for series in ( + pl.Series(["a", None, "b"]), + pl.Series([True, None, False]), + pl.Series([1, None, 2]), + pl.Series(["a", None, "b"], dtype=pl.Categorical), + ): + np.testing.assert_equal( + is_nan(series).to_numpy(), + np.array([False, False, False]), + ) + np.testing.assert_equal( + is_nan_or_none(series).to_numpy(), + np.array([False, True, False]), + ) def test_is_none(): @@ -117,17 +131,6 @@ def test_is_nan_or_none(): is_nan_or_none(pl.Series([np.nan, 1.0, None])).to_numpy(), np.array([True, False, True]), ) - for values in (["a", None, "b"], [True, None, False], [1, None, 2]): - np.testing.assert_equal( - is_nan_or_none(pl.Series(values)).to_numpy(), - np.array([False, True, False]), - ) - np.testing.assert_equal( - is_nan_or_none( - pl.Series(["a", None, "b"], dtype=pl.Categorical) - ).to_numpy(), - np.array([False, True, False]), - ) def test_vertical_concat_pd(): diff --git a/utilsforecast/processing.py b/utilsforecast/processing.py index 75b38ba..691fcfd 100644 --- a/utilsforecast/processing.py +++ b/utilsforecast/processing.py @@ -233,8 +233,11 @@ def filter_with_mask( def is_nan(s: Series) -> Series: if isinstance(s, pd.Series): out = s.isna() - else: + elif s.dtype.is_float(): out = s.is_nan() + else: + # polars only supports is_nan on numeric columns and only floats can hold NaN + out = pl.repeat(False, s.len(), eager=True).alias(s.name) return out @@ -247,10 +250,6 @@ def is_none(s: Series) -> Series: def is_nan_or_none(s: Series) -> Series: - if isinstance(s, pl_Series) and not s.dtype.is_float(): - # polars only supports is_nan on numeric columns and only floats - # can hold NaN, so other columns (e.g. strings) can only have nulls - return s.is_null() return is_nan(s) | is_none(s)