Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose half_up rounding in cuDF #8477

Merged
merged 2 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions python/cudf/cudf/_lib/round.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ from cudf._lib.cpp.round cimport (
)


def round(Column input_col, int decimal_places=0):
def round(Column input_col, int decimal_places=0, how="half_even"):
"""
Round column values to the given number of decimal places

Expand All @@ -26,16 +26,21 @@ def round(Column input_col, int decimal_places=0):
-------
A Column with values rounded to the given number of decimal places
"""
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 = (
Copy link
Contributor

@cwharris cwharris Jun 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will there ever be more than 2 rounding methods? if so, we might create a breaking change for anyone who mis-spelled the non-default argument.

in that case, I'd suggest throwing an error if either of these rounding methods are not an exact match, so that breaking change scenario can never happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

cpp_rounding_method.HALF_EVEN if how == "half_even"
else cpp_rounding_method.HALF_UP
)
with nogil:
c_result = move(
cpp_round(
input_col_view,
decimal_places,
cpp_rounding_method.HALF_EVEN,
c_how
)
)

Expand Down
6 changes: 4 additions & 2 deletions python/cudf/cudf/core/column/numerical_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,12 @@ def corr(self, other: ColumnBase) -> float:
return cudf.utils.dtypes._get_nan_for_dtype(self.dtype)
return cov / lhs_std / rhs_std

def round(self, decimals: int = 0) -> NumericalBaseColumn:
def round(
self, decimals: int = 0, how: str = "half_even"
) -> NumericalBaseColumn:
"""Round the values in the Column to the given number of decimals.
"""
return libcudf.round.round(self, decimal_places=decimals)
return libcudf.round.round(self, decimal_places=decimals, how=how)

def _apply_scan_op(self, op: str) -> ColumnBase:
return self._copy_type_metadata(libcudf.reduce.scan(op, self, True))
9 changes: 6 additions & 3 deletions python/cudf/cudf/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ def __arrow_array__(self, type=None):
"consider using .to_arrow()"
)

def round(self, decimals=0):
def round(self, decimals=0, how="half_even"):
"""
Round a DataFrame to a variable number of decimal places.

Expand All @@ -1676,6 +1676,9 @@ def round(self, decimals=0):
columns not included in `decimals` will be left as is. Elements
of `decimals` which are not columns of the input will be
ignored.
how : str, optional
Type of rounding. Can be either "half_even" (default)
of "half_up" rounding.

Returns
-------
Expand Down Expand Up @@ -1741,7 +1744,7 @@ def round(self, decimals=0):
raise ValueError("Index of decimals must be unique")

cols = {
name: col.round(decimals[name])
name: col.round(decimals[name], how=how)
if (
name in decimals.keys()
and pd.api.types.is_numeric_dtype(col.dtype)
Expand All @@ -1751,7 +1754,7 @@ def round(self, decimals=0):
}
elif isinstance(decimals, int):
cols = {
name: col.round(decimals)
name: col.round(decimals, how=how)
if pd.api.types.is_numeric_dtype(col.dtype)
else col.copy(deep=True)
for name, col in self._data.items()
Expand Down
8 changes: 5 additions & 3 deletions python/cudf/cudf/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4748,7 +4748,7 @@ def mode(self, dropna=True):

return Series(val_counts.index.sort_values(), name=self.name)

def round(self, decimals=0):
def round(self, decimals=0, how="half_even"):
"""
Round each value in a Series to the given number of decimals.

Expand All @@ -4758,6 +4758,9 @@ def round(self, decimals=0):
Number of decimal places to round to. If decimals is negative,
it specifies the number of positions to the left of the decimal
point.
how : str, optional
Type of rounding. Can be either "half_even" (default)
of "half_up" rounding.

Returns
-------
Expand All @@ -4773,9 +4776,8 @@ def round(self, decimals=0):
2 3.0
dtype: float64
"""

return Series(
self._column.round(decimals=decimals),
self._column.round(decimals=decimals, how=how),
name=self.name,
index=self.index,
dtype=self.dtype,
Expand Down
7 changes: 7 additions & 0 deletions python/cudf/cudf/tests/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,13 @@ def test_series_round(arr, decimals):
np.array_equal(ser.nullmask.to_array(), result.to_array())


def test_series_round_half_up():
s = cudf.Series([0.0, 1.0, 1.2, 1.7, 0.5, 1.5, 2.5, None])
expect = cudf.Series([0.0, 1.0, 1.0, 2.0, 1.0, 2.0, 3.0, None])
got = s.round(how="half_up")
isVoid marked this conversation as resolved.
Show resolved Hide resolved
assert_eq(expect, got)


@pytest.mark.parametrize(
"series",
[
Expand Down