Skip to content
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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ Other enhancements
- Added :meth:`DataFrame.from_arrow` and :meth:`Series.from_arrow` to import any Arrow-compatible
data object into a pandas object through the
`Arrow PyCapsule Protocol <https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html>`__ (:issue:`59631`)
- :meth:`Series.round` now supports object dtypes when the underlying Python objects implement ``__round__`` (:issue:`63444`)

.. ---------------------------------------------------------------------------
.. _whatsnew_300.notable_bug_fixes:
Expand Down
13 changes: 11 additions & 2 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2577,9 +2577,18 @@ def round(self, decimals: int = 0, *args, **kwargs) -> Series:
4 3.0
dtype: float64
"""

nv.validate_round(args, kwargs)
if self.dtype == "object":
raise TypeError("Expected numeric dtype, got object instead.")

if len(self) == 0:
return self.copy()

if is_object_dtype(self.dtype):
values = self._values
result = lib.map_infer(values, lambda x: round(x, decimals), convert=False)
Comment thread
sanrishi marked this conversation as resolved.
return self._constructor(result, index=self.index, copy=False).__finalize__(
self, method="round"
)
new_mgr = self._mgr.round(decimals=decimals)
return self._constructor_from_mgr(new_mgr, axes=new_mgr.axes).__finalize__(
self, method="round"
Expand Down
50 changes: 44 additions & 6 deletions pandas/tests/series/methods/test_round.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,47 @@ def test_round_ea_boolean(self):
result.iloc[0] = False
tm.assert_series_equal(ser, expected)

def test_round_dtype_object(self):
# GH#61206
ser = Series([0.2], dtype="object")
msg = "Expected numeric dtype, got object instead."
with pytest.raises(TypeError, match=msg):
ser.round()
@pytest.mark.parametrize(
"data,decimals,expected_data",
[
([0.2], 0, [0.0]),
([1.234, 2.567], 1, [1.2, 2.6]),
([1.234, 2.567], 2, [1.23, 2.57]),
([1.234, 2.567], 0, [1.0, 3.0]),
],
)
def test_round_dtype_object(self, data, decimals, expected_data):
# GH#63444
ser = Series(data, dtype="object")
Comment thread
sanrishi marked this conversation as resolved.
result = ser.round(decimals)
expected = Series(expected_data, dtype="object")
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize(
"dtype",
[
None,
"float64",
"int64",
"object",
],
)
def test_round_empty_series(self, dtype):
# GH#63444
result = Series(dtype=dtype).round(4)
expected = Series(dtype=dtype)
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize(
"dtype,data",
[
("string", ["a", "b", "c"]),
("category", ["cat1", "cat2", "cat3"]),
(pd.StringDtype(), ["x", "y", "z"]),
],
)
def test_round_non_numeric_dtype_noop(self, dtype, data):
# GH#63444, GH#63559
ser = Series(data, dtype=dtype)
result = ser.round(2)
tm.assert_series_equal(result, ser)
Loading