Skip to content

Commit ad96a28

Browse files
committed
core: coerce result to dtype in Block.eval if dtype explicitly given
1 parent 322dbf4 commit ad96a28

File tree

3 files changed

+24
-7
lines changed

3 files changed

+24
-7
lines changed

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ Datetimelike
436436

437437
- Fixed bug where two :class:`DateOffset` objects with different ``normalize`` attributes could evaluate as equal (:issue:`21404`)
438438
- Fixed bug where :meth:`Timestamp.resolution` incorrectly returned 1-microsecond ``timedelta`` instead of 1-nanosecond :class:`Timedelta` (:issue:`21336`,:issue:`21365`)
439+
- Fixed bug where :class:`DataFrame` with ``dtype='datetime64[ns]'`` operating with :class:`DateOffset` could cast to ``dtype='object'`` (:issue:`21610`)
439440

440441
Timedelta
441442
^^^^^^^^^

pandas/core/internals.py

+18-7
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
is_float_dtype, is_numeric_dtype,
3636
is_numeric_v_string_like, is_extension_type,
3737
is_extension_array_dtype,
38-
is_list_like,
38+
is_list_like, is_offsetlike,
3939
is_re,
4040
is_re_compilable,
4141
is_scalar,
@@ -710,6 +710,10 @@ def _try_cast_result(self, result, dtype=None):
710710
""" try to cast the result to our original type, we may have
711711
roundtripped thru object in the mean-time
712712
"""
713+
# coerce to `dtype` if explicitly given
714+
if isinstance(dtype, np.dtype):
715+
return result.astype(dtype)
716+
713717
if dtype is None:
714718
dtype = self.dtype
715719

@@ -1315,7 +1319,8 @@ def shift(self, periods, axis=0, mgr=None):
13151319

13161320
return [self.make_block(new_values)]
13171321

1318-
def eval(self, func, other, errors='raise', try_cast=False, mgr=None):
1322+
def eval(self, func, other, errors='raise', try_cast=False, mgr=None,
1323+
dtype=None):
13191324
"""
13201325
evaluate the block; return result block from the result
13211326
@@ -1327,7 +1332,8 @@ def eval(self, func, other, errors='raise', try_cast=False, mgr=None):
13271332
- ``raise`` : allow exceptions to be raised
13281333
- ``ignore`` : suppress exceptions. On error return original object
13291334
1330-
try_cast : try casting the results to the input type
1335+
try_cast : try casting the results to ``dtype`` or the input type
1336+
dtype: if not ``None`` and ``try_cast=Ture``, coerce the result to it
13311337
13321338
Returns
13331339
-------
@@ -1362,10 +1368,15 @@ def eval(self, func, other, errors='raise', try_cast=False, mgr=None):
13621368
values, values_mask, other, other_mask = self._try_coerce_args(
13631369
transf(values), other)
13641370
except TypeError:
1371+
# `Timestamp` operate with `DateOffset` must cast to `object`,
1372+
# then cast back to `datetime64[ns]` after operation.
1373+
if (dtype is None and is_datetime64_dtype(self)
1374+
and is_offsetlike(other)):
1375+
dtype = self.dtype
1376+
13651377
block = self.coerce_to_target_dtype(orig_other)
1366-
return block.eval(func, orig_other,
1367-
errors=errors,
1368-
try_cast=try_cast, mgr=mgr)
1378+
return block.eval(func, orig_other, errors=errors,
1379+
try_cast=try_cast, mgr=mgr, dtype=dtype)
13691380

13701381
# get the result, may need to transpose the other
13711382
def get_result(other):
@@ -1449,7 +1460,7 @@ def handle_error():
14491460

14501461
# try to cast if requested
14511462
if try_cast:
1452-
result = self._try_cast_result(result)
1463+
result = self._try_cast_result(result, dtype=dtype)
14531464

14541465
result = _block_shape(result, ndim=self.ndim)
14551466
return [self.make_block(result)]

pandas/tests/frame/test_arithmetic.py

+5
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ def test_df_sub_datetime64_not_ns(self):
211211
pd.Timedelta(days=2)])
212212
tm.assert_frame_equal(res, expected)
213213

214+
def test_timestamp_df_add_dateoffset(self):
215+
expected = pd.DataFrame([pd.Timestamp('2019')])
216+
result = pd.DataFrame([pd.Timestamp('2018')]) + pd.DateOffset(years=1)
217+
tm.assert_frame_equal(result, expected)
218+
214219
@pytest.mark.parametrize('data', [
215220
[1, 2, 3],
216221
[1.1, 2.2, 3.3],

0 commit comments

Comments
 (0)