"""Integration tests for arithmetic expression operations. These tests require Ray and test end-to-end arithmetic expression evaluation. """ import math import pandas as pd import pytest from packaging.version import parse as parse_version import ray from ray.data._internal.util import rows_same from ray.data._internal.utils.arrow_utils import get_pyarrow_version from ray.data.expressions import col, lit from ray.data.tests.conftest import * # noqa from ray.tests.conftest import * # noqa pytestmark = pytest.mark.skipif( get_pyarrow_version() < parse_version("20.0.0"), reason="Expression integration tests require PyArrow >= 20.0.0", ) class TestArithmeticIntegration: """Integration tests for arithmetic expressions with Ray Dataset.""" def test_arithmetic_with_dataset(self, ray_start_regular_shared): """Test arithmetic expressions work correctly with Ray Dataset.""" ds = ray.data.from_items( [ {"price": 10.0, "quantity": 2}, {"price": 20.0, "quantity": 3}, {"price": 15.0, "quantity": 4}, ] ) result = ds.with_column("total", col("price") * col("quantity")).to_pandas() expected = pd.DataFrame( { "price": [10.0, 20.0, 15.0], "quantity": [2, 3, 4], "total": [20.0, 60.0, 60.0], } ) assert rows_same(result, expected) def test_chained_arithmetic_with_dataset(self, ray_start_regular_shared): """Test chained arithmetic expressions with Ray Dataset.""" ds = ray.data.from_items( [ {"a": 10, "b": 5}, {"a": 20, "b": 3}, ] ) result = ( ds.with_column("sum", col("a") + col("b")) .with_column("diff", col("a") - col("b")) .with_column("product", col("a") * col("b")) .to_pandas() ) expected = pd.DataFrame( { "a": [10, 20], "b": [5, 3], "sum": [15, 23], "diff": [5, 17], "product": [50, 60], } ) assert rows_same(result, expected) def test_floor_division_with_dataset(self, ray_start_regular_shared): """Test floor division operations with Ray Dataset.""" ds = ray.data.range(5) result = ds.with_column("result", col("id") // 2).to_pandas() expected = pd.DataFrame({"id": [0, 1, 2, 3, 4], "result": [0, 0, 1, 1, 2]}) assert rows_same(result, expected) def test_literal_floor_division_with_dataset(self, ray_start_regular_shared): """Test literal floor division by expression with Ray Dataset.""" ds = ray.data.range(5) result = ds.with_column("result", lit(10) // (col("id") + 2)).to_pandas() expected = pd.DataFrame({"id": [0, 1, 2, 3, 4], "result": [5, 3, 2, 2, 1]}) assert rows_same(result, expected) @pytest.mark.parametrize( "expr_factory,expected_values", [ pytest.param(lambda: col("value").ceil(), [-1, 0, 0, 1, 2], id="ceil"), pytest.param(lambda: col("value").floor(), [-2, -1, 0, 0, 1], id="floor"), pytest.param(lambda: col("value").round(), [-2, 0, 0, 0, 2], id="round"), pytest.param(lambda: col("value").trunc(), [-1, 0, 0, 0, 1], id="trunc"), ], ) def test_rounding_with_dataset( self, ray_start_regular_shared, expr_factory, expected_values ): """Test rounding operations with Ray Dataset.""" values = [-1.75, -0.25, 0.0, 0.25, 1.75] ds = ray.data.from_items([{"value": v} for v in values]) result = ds.with_column("result", expr_factory()).to_pandas() expected = pd.DataFrame({"value": values, "result": expected_values}) assert rows_same(result, expected) @pytest.mark.parametrize( "expr_factory,expected_fn", [ pytest.param(lambda: col("value").ln(), math.log, id="ln"), pytest.param(lambda: col("value").log10(), math.log10, id="log10"), pytest.param(lambda: col("value").log2(), math.log2, id="log2"), pytest.param(lambda: col("value").exp(), math.exp, id="exp"), ], ) def test_logarithmic_with_dataset( self, ray_start_regular_shared, expr_factory, expected_fn ): """Test logarithmic operations with Ray Dataset.""" values = [1.0, math.e, 10.0, 4.0] ds = ray.data.from_items([{"value": v} for v in values]) expected_values = [expected_fn(v) for v in values] result = ds.with_column("result", expr_factory()).to_pandas() expected = pd.DataFrame({"value": values, "result": expected_values}) assert rows_same(result, expected) @pytest.mark.parametrize( "expr_factory,expected_fn", [ pytest.param(lambda: col("value").sin(), math.sin, id="sin"), pytest.param(lambda: col("value").cos(), math.cos, id="cos"), pytest.param(lambda: col("value").tan(), math.tan, id="tan"), pytest.param(lambda: col("value").atan(), math.atan, id="atan"), ], ) def test_trigonometric_with_dataset( self, ray_start_regular_shared, expr_factory, expected_fn ): """Test trigonometric operations with Ray Dataset.""" values = [0.0, math.pi / 6, math.pi / 4, math.pi / 3] ds = ray.data.from_items([{"value": v} for v in values]) expected_values = [expected_fn(v) for v in values] result = ds.with_column("result", expr_factory()).to_pandas() expected = pd.DataFrame({"value": values, "result": expected_values}) assert rows_same(result, expected) @pytest.mark.parametrize( "test_data,expr_factory,expected_results", [ pytest.param( [{"x": 5}, {"x": -3}, {"x": 0}], lambda: col("x").negate(), [-5, 3, 0], id="negate", ), pytest.param( [{"x": 5}, {"x": -3}, {"x": 0}], lambda: col("x").sign(), [1, -1, 0], id="sign", ), pytest.param( [{"x": 5}, {"x": -3}, {"x": 0}], lambda: col("x").abs(), [5, 3, 0], id="abs", ), pytest.param( [{"x": 2}, {"x": 3}, {"x": 4}], lambda: col("x").power(2), [4, 9, 16], id="power_int", ), pytest.param( [{"x": 4}, {"x": 9}, {"x": 16}], lambda: col("x").power(0.5), [2.0, 3.0, 4.0], id="power_sqrt", ), ], ) def test_arithmetic_helpers_with_dataset( self, ray_start_regular_shared, test_data, expr_factory, expected_results ): """Test arithmetic helper operations with Ray Dataset.""" ds = ray.data.from_items(test_data) result = ds.with_column("result", expr_factory()).to_pandas() expected = pd.DataFrame(test_data) expected["result"] = expected_results assert rows_same(result, expected) def test_age_group_calculation_with_dataset(self, ray_start_regular_shared): """Test floor division for grouping values (e.g., age into decades).""" test_data = [ {"age": 25}, {"age": 17}, {"age": 30}, ] ds = ray.data.from_items(test_data) result = ds.with_column("age_group", col("age") // 10 * 10).to_pandas() expected = pd.DataFrame({"age": [25, 17, 30], "age_group": [20, 10, 30]}) assert rows_same(result, expected) if __name__ == "__main__": import sys sys.exit(pytest.main(["-v", __file__]))