diff --git a/docs/cudf/source/user_guide/api_docs/pylibcudf/index.rst b/docs/cudf/source/user_guide/api_docs/pylibcudf/index.rst index 1c1b37e2c37..26875ce7d12 100644 --- a/docs/cudf/source/user_guide/api_docs/pylibcudf/index.rst +++ b/docs/cudf/source/user_guide/api_docs/pylibcudf/index.rst @@ -22,6 +22,7 @@ This page provides API documentation for pylibcudf. reduce reshape rolling + round scalar search stream_compaction diff --git a/docs/cudf/source/user_guide/api_docs/pylibcudf/round.rst b/docs/cudf/source/user_guide/api_docs/pylibcudf/round.rst new file mode 100644 index 00000000000..c97fda12301 --- /dev/null +++ b/docs/cudf/source/user_guide/api_docs/pylibcudf/round.rst @@ -0,0 +1,6 @@ +===== +round +===== + +.. automodule:: cudf._lib.pylibcudf.round + :members: diff --git a/python/cudf/cudf/_lib/pylibcudf/CMakeLists.txt b/python/cudf/cudf/_lib/pylibcudf/CMakeLists.txt index 7d01671e84f..eff14ad549b 100644 --- a/python/cudf/cudf/_lib/pylibcudf/CMakeLists.txt +++ b/python/cudf/cudf/_lib/pylibcudf/CMakeLists.txt @@ -29,6 +29,7 @@ set(cython_sources replace.pyx reshape.pyx rolling.pyx + round.pyx scalar.pyx search.pyx stream_compaction.pyx diff --git a/python/cudf/cudf/_lib/pylibcudf/__init__.pxd b/python/cudf/cudf/_lib/pylibcudf/__init__.pxd index 91c3fdf5602..4f77f8cbaef 100644 --- a/python/cudf/cudf/_lib/pylibcudf/__init__.pxd +++ b/python/cudf/cudf/_lib/pylibcudf/__init__.pxd @@ -15,6 +15,7 @@ from . cimport ( replace, reshape, rolling, + round, search, sorting, stream_compaction, @@ -48,6 +49,7 @@ __all__ = [ "reduce", "replace", "rolling", + "round", "search", "stream_compaction", "strings", diff --git a/python/cudf/cudf/_lib/pylibcudf/__init__.py b/python/cudf/cudf/_lib/pylibcudf/__init__.py index fcdc4992f00..048b62b6013 100644 --- a/python/cudf/cudf/_lib/pylibcudf/__init__.py +++ b/python/cudf/cudf/_lib/pylibcudf/__init__.py @@ -15,6 +15,7 @@ replace, reshape, rolling, + round, search, sorting, stream_compaction, @@ -48,6 +49,7 @@ "reduce", "replace", "rolling", + "round", "search", "stream_compaction", "strings", diff --git a/python/cudf/cudf/_lib/pylibcudf/libcudf/CMakeLists.txt b/python/cudf/cudf/_lib/pylibcudf/libcudf/CMakeLists.txt index 8a6ce6a5187..ac56d42dda8 100644 --- a/python/cudf/cudf/_lib/pylibcudf/libcudf/CMakeLists.txt +++ b/python/cudf/cudf/_lib/pylibcudf/libcudf/CMakeLists.txt @@ -12,7 +12,7 @@ # the License. # ============================================================================= -set(cython_sources aggregation.pyx binaryop.pyx copying.pyx replace.pyx reduce.pxd +set(cython_sources aggregation.pyx binaryop.pyx copying.pyx replace.pyx reduce.pxd round.pyx stream_compaction.pyx types.pyx unary.pyx ) diff --git a/python/cudf/cudf/_lib/pylibcudf/libcudf/round.pxd b/python/cudf/cudf/_lib/pylibcudf/libcudf/round.pxd index 06ff42485ea..027c4634c9f 100644 --- a/python/cudf/cudf/_lib/pylibcudf/libcudf/round.pxd +++ b/python/cudf/cudf/_lib/pylibcudf/libcudf/round.pxd @@ -9,9 +9,9 @@ from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view cdef extern from "cudf/round.hpp" namespace "cudf" nogil: - ctypedef enum rounding_method "cudf::rounding_method": - HALF_UP "cudf::rounding_method::HALF_UP" - HALF_EVEN "cudf::rounding_method::HALF_EVEN" + cpdef enum class rounding_method(int32_t): + HALF_UP + HALF_EVEN cdef unique_ptr[column] round ( const column_view& input, diff --git a/python/cudf/cudf/_lib/pylibcudf/libcudf/round.pyx b/python/cudf/cudf/_lib/pylibcudf/libcudf/round.pyx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/cudf/cudf/_lib/pylibcudf/round.pxd b/python/cudf/cudf/_lib/pylibcudf/round.pxd new file mode 100644 index 00000000000..ccb64fc2847 --- /dev/null +++ b/python/cudf/cudf/_lib/pylibcudf/round.pxd @@ -0,0 +1,13 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. +from libc.stdint cimport int32_t + +from cudf._lib.pylibcudf.libcudf.round cimport rounding_method + +from .column cimport Column + + +cpdef Column round( + Column source, + int32_t decimal_places = *, + rounding_method round_method = * +) diff --git a/python/cudf/cudf/_lib/pylibcudf/round.pyx b/python/cudf/cudf/_lib/pylibcudf/round.pyx new file mode 100644 index 00000000000..cfcc2aafbb8 --- /dev/null +++ b/python/cudf/cudf/_lib/pylibcudf/round.pyx @@ -0,0 +1,54 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. +from libc.stdint cimport int32_t +from libcpp.memory cimport unique_ptr +from libcpp.utility cimport move + +from cudf._lib.pylibcudf.libcudf.round cimport ( + round as cpp_round, + rounding_method, +) + +from cudf._lib.pylibcudf.libcudf.round import \ + rounding_method as RoundingMethod # no-cython-lint + +from cudf._lib.pylibcudf.libcudf.column.column cimport column + +from .column cimport Column + + +cpdef Column round( + Column source, + int32_t decimal_places = 0, + rounding_method round_method = rounding_method.HALF_UP +): + """Rounds all the values in a column to the specified number of decimal places. + + For details, see :cpp:func:`round`. + + Parameters + ---------- + source : Column + The Column for which to round values. + decimal_places: int32_t, optional + The number of decimal places to round to (default 0) + round_method: rounding_method, optional + The method by which to round each value. + Can be one of { RoundingMethod.HALF_UP, RoundingMethod.HALF_EVEN } + (default rounding_method.HALF_UP) + + Returns + ------- + pylibcudf.Column + A Column with values rounded + """ + cdef unique_ptr[column] c_result + with nogil: + c_result = move( + cpp_round( + source.view(), + decimal_places, + round_method + ) + ) + + return Column.from_libcudf(move(c_result)) diff --git a/python/cudf/cudf/_lib/round.pyx b/python/cudf/cudf/_lib/round.pyx index c1c36dd8854..f8ad57947c8 100644 --- a/python/cudf/cudf/_lib/round.pyx +++ b/python/cudf/cudf/_lib/round.pyx @@ -2,16 +2,10 @@ from cudf.core.buffer import acquire_spill_lock -from libcpp.memory cimport unique_ptr -from libcpp.utility cimport move - from cudf._lib.column cimport Column -from cudf._lib.pylibcudf.libcudf.column.column cimport column -from cudf._lib.pylibcudf.libcudf.column.column_view cimport column_view -from cudf._lib.pylibcudf.libcudf.round cimport ( - round as cpp_round, - rounding_method as cpp_rounding_method, -) + +import cudf._lib.pylibcudf as plc +from cudf._lib.pylibcudf.round import RoundingMethod @acquire_spill_lock() @@ -31,19 +25,15 @@ def round(Column input_col, int decimal_places=0, how="half_even"): if how not in {"half_even", "half_up"}: raise ValueError("'how' must be either 'half_even' or 'half_up'") - cdef column_view input_col_view = input_col.view() - cdef unique_ptr[column] c_result - cdef cpp_rounding_method c_how = ( - cpp_rounding_method.HALF_EVEN if how == "half_even" - else cpp_rounding_method.HALF_UP + how = ( + RoundingMethod.HALF_EVEN if how == "half_even" + else RoundingMethod.HALF_UP ) - with nogil: - c_result = move( - cpp_round( - input_col_view, - decimal_places, - c_how - ) - ) - return Column.from_unique_ptr(move(c_result)) + return Column.from_pylibcudf( + plc.round.round( + input_col.to_pylibcudf(mode="read"), + decimal_places, + how + ) + ) diff --git a/python/cudf/cudf/pylibcudf_tests/test_round.py b/python/cudf/cudf/pylibcudf_tests/test_round.py new file mode 100644 index 00000000000..a234860477f --- /dev/null +++ b/python/cudf/cudf/pylibcudf_tests/test_round.py @@ -0,0 +1,38 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. + +import pyarrow as pa +import pytest +from utils import assert_column_eq + +import cudf._lib.pylibcudf as plc + + +@pytest.fixture(params=[False, True]) +def nullable(request): + return request.param + + +@pytest.fixture(params=["float32", "float64"]) +def column(request, nullable): + values = [2.5, 2.49, 1.6, 8, -1.5, -1.7, -0.5, 0.5] + typ = {"float32": pa.float32(), "float64": pa.float64()}[request.param] + if nullable: + values[2] = None + return plc.interop.from_arrow(pa.array(values, type=typ)) + + +@pytest.mark.parametrize( + "round_mode", ["half_towards_infinity", "half_to_even"] +) +@pytest.mark.parametrize("decimals", [0, 1, 2, 5]) +def test_round(column, round_mode, decimals): + method = { + "half_towards_infinity": plc.round.RoundingMethod.HALF_UP, + "half_to_even": plc.round.RoundingMethod.HALF_EVEN, + }[round_mode] + got = plc.round.round(column, decimals, method) + expect = pa.compute.round( + plc.interop.to_arrow(column), decimals, round_mode + ) + + assert_column_eq(expect, got)