From b1f15c3a584a876c8e6f671c510c1885a4aecdef Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Wed, 4 Sep 2024 13:30:29 +0200 Subject: [PATCH] fix: Empty any_horizontal should be false, not true (#18545) --- .../polars-plan/src/plans/conversion/functions.rs | 7 ++++--- .../operations/aggregation/test_horizontal.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/polars-plan/src/plans/conversion/functions.rs b/crates/polars-plan/src/plans/conversion/functions.rs index 409da958477d..f77cb0e79a95 100644 --- a/crates/polars-plan/src/plans/conversion/functions.rs +++ b/crates/polars-plan/src/plans/conversion/functions.rs @@ -16,11 +16,12 @@ pub(super) fn convert_functions( ) -> PolarsResult { match function { // This can be created by col(*).is_null() on empty dataframes. - FunctionExpr::Boolean(BooleanFunction::AllHorizontal | BooleanFunction::AnyHorizontal) - if input.is_empty() => - { + FunctionExpr::Boolean(BooleanFunction::AllHorizontal) if input.is_empty() => { return to_aexpr_impl(lit(true), arena, state); }, + FunctionExpr::Boolean(BooleanFunction::AnyHorizontal) if input.is_empty() => { + return to_aexpr_impl(lit(false), arena, state); + }, // Convert to binary expression as the optimizer understands those. // Don't exceed 128 expressions as we might stackoverflow. FunctionExpr::Boolean(BooleanFunction::AllHorizontal) => { diff --git a/py-polars/tests/unit/operations/aggregation/test_horizontal.py b/py-polars/tests/unit/operations/aggregation/test_horizontal.py index bc2bf2abbf46..b4054c695979 100644 --- a/py-polars/tests/unit/operations/aggregation/test_horizontal.py +++ b/py-polars/tests/unit/operations/aggregation/test_horizontal.py @@ -7,6 +7,7 @@ import pytest import polars as pl +import polars.selectors as cs from polars.exceptions import ComputeError from polars.testing import assert_frame_equal, assert_series_equal @@ -51,6 +52,20 @@ def test_all_any_horizontally() -> None: assert "horizontal" not in dfltr.explain().lower() +def test_empty_all_any_horizontally() -> None: + # any/all_horizontal don't allow empty input, but we can still trigger this + # by selecting an empty set of columns with pl.selectors. + df = pl.DataFrame({"x": [1, 2, 3]}) + assert_frame_equal( + df.select(pl.any_horizontal(cs.string().is_null())), + pl.DataFrame({"literal": False}), + ) + assert_frame_equal( + df.select(pl.all_horizontal(cs.string().is_null())), + pl.DataFrame({"literal": True}), + ) + + def test_all_any_single_input() -> None: df = pl.DataFrame({"a": [0, 1, None]}) out = df.select(