|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import pytest |
| 4 | + |
| 5 | +import narwhals as nw |
| 6 | +from tests.utils import PANDAS_VERSION, Constructor, ConstructorEager, assert_equal_data |
| 7 | + |
| 8 | + |
| 9 | +@pytest.mark.parametrize( |
| 10 | + ("left", "right", "expected"), |
| 11 | + [ |
| 12 | + (-2, 0, float("-inf")), |
| 13 | + (-2.0, 0.0, float("-inf")), |
| 14 | + (0, 0, None), |
| 15 | + (0.0, 0.0, None), |
| 16 | + (2, 0, float("inf")), |
| 17 | + (2.0, 0.0, float("inf")), |
| 18 | + ], |
| 19 | +) |
| 20 | +def test_series_truediv_by_zero( |
| 21 | + left: float, right: float, expected: float | None, constructor_eager: ConstructorEager |
| 22 | +) -> None: |
| 23 | + data: dict[str, list[int | float]] = {"a": [left], "b": [right]} |
| 24 | + df = nw.from_native(constructor_eager(data), eager_only=True) |
| 25 | + truediv_result = df["a"] / df["b"] |
| 26 | + assert_equal_data({"a": truediv_result}, {"a": [expected]}) |
| 27 | + |
| 28 | + |
| 29 | +@pytest.mark.parametrize( |
| 30 | + ("left", "right", "expected"), |
| 31 | + [(-2, 0, float("-inf")), (0, 0, None), (2, 0, float("inf"))], |
| 32 | +) |
| 33 | +@pytest.mark.skipif(PANDAS_VERSION < (2, 0), reason="converts floordiv by zero to 0") |
| 34 | +def test_series_floordiv_int_by_zero( |
| 35 | + left: int, |
| 36 | + right: int, |
| 37 | + expected: float | None, |
| 38 | + constructor_eager: ConstructorEager, |
| 39 | + request: pytest.FixtureRequest, |
| 40 | +) -> None: |
| 41 | + data: dict[str, list[int]] = {"a": [left], "b": [right]} |
| 42 | + df = nw.from_native(constructor_eager(data), eager_only=True) |
| 43 | + # pyarrow backend floordiv raises divide by zero error |
| 44 | + if "pyarrow" in str(constructor_eager): |
| 45 | + request.applymarker(pytest.mark.xfail) |
| 46 | + # polars backend floordiv by zero always returns null |
| 47 | + if "polars" in str(constructor_eager): |
| 48 | + floordiv_result = df["a"] // df["b"] |
| 49 | + assert all(floordiv_result.is_null()) |
| 50 | + # pandas[nullable] backend floordiv always returns 0 |
| 51 | + elif all(x in str(constructor_eager) for x in ["pandas", "nullable"]): |
| 52 | + floordiv_result = df["a"] // df["b"] |
| 53 | + assert_equal_data({"a": floordiv_result}, {"a": [0]}) |
| 54 | + else: |
| 55 | + floordiv_result = df["a"] // df["b"] |
| 56 | + assert_equal_data({"a": floordiv_result}, {"a": [expected]}) |
| 57 | + |
| 58 | + |
| 59 | +@pytest.mark.parametrize( |
| 60 | + ("left", "right", "expected"), |
| 61 | + [ |
| 62 | + (-2, 0, float("-inf")), |
| 63 | + (-2.0, 0.0, float("-inf")), |
| 64 | + (0, 0, None), |
| 65 | + (0.0, 0.0, None), |
| 66 | + (2, 0, float("inf")), |
| 67 | + (2.0, 0.0, float("inf")), |
| 68 | + ], |
| 69 | +) |
| 70 | +def test_truediv_by_zero( |
| 71 | + left: float, right: float, expected: float | None, constructor: Constructor |
| 72 | +) -> None: |
| 73 | + data: dict[str, list[int | float]] = {"a": [left]} |
| 74 | + df = nw.from_native(constructor(data)) |
| 75 | + truediv_result = df.select(nw.col("a") / right) |
| 76 | + assert_equal_data(truediv_result, {"a": [expected]}) |
| 77 | + |
| 78 | + |
| 79 | +@pytest.mark.parametrize( |
| 80 | + ("left", "right", "expected"), |
| 81 | + [(-2, 0, float("-inf")), (0, 0, None), (2, 0, float("inf"))], |
| 82 | +) |
| 83 | +@pytest.mark.skipif(PANDAS_VERSION < (2, 0), reason="converts floordiv by zero to 0") |
| 84 | +def test_floordiv_int_by_zero( |
| 85 | + left: int, |
| 86 | + right: int, |
| 87 | + expected: float | None, |
| 88 | + constructor: Constructor, |
| 89 | + request: pytest.FixtureRequest, |
| 90 | +) -> None: |
| 91 | + data: dict[str, list[int]] = {"a": [left]} |
| 92 | + df = nw.from_native(constructor(data)) |
| 93 | + # pyarrow backend floordiv raises divide by zero error |
| 94 | + # ibis backend floordiv cannot cast value to inf or -inf |
| 95 | + if any(x in str(constructor) for x in ["ibis", "pyarrow"]): |
| 96 | + request.applymarker(pytest.mark.xfail) |
| 97 | + # duckdb backend floordiv return None |
| 98 | + if "duckdb" in str(constructor): |
| 99 | + floordiv_result = df.select(nw.col("a") // right) |
| 100 | + assert_equal_data(floordiv_result, {"a": [None]}) |
| 101 | + # polars backend floordiv returns null |
| 102 | + elif "polars" in str(constructor) and "lazy" not in str(constructor): |
| 103 | + floordiv_result = df.select(nw.col("a") // right) |
| 104 | + assert all(floordiv_result["a"].is_null()) |
| 105 | + # polars lazy floordiv cannot be sliced and returns None |
| 106 | + elif all(x in str(constructor) for x in ["polars", "lazy"]): |
| 107 | + floordiv_result = df.select(nw.col("a") // right) |
| 108 | + assert_equal_data(floordiv_result, {"a": [None]}) |
| 109 | + # pandas[nullable] backend floordiv always returns 0 |
| 110 | + elif all(x in str(constructor) for x in ["pandas", "nullable"]): |
| 111 | + floordiv_result = df.select(nw.col("a") // right) |
| 112 | + assert_equal_data(floordiv_result, {"a": [0]}) |
| 113 | + else: |
| 114 | + floordiv_result = df.select(nw.col("a") // right) |
| 115 | + assert_equal_data(floordiv_result, {"a": [expected]}) |
0 commit comments