Skip to content

Commit 53be520

Browse files
authored
COMPAT: properly handle objects of datetimelike in astype_nansafe (#19232)
reclose #19116
1 parent 0477880 commit 53be520

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

pandas/core/dtypes/cast.py

+20-7
Original file line numberDiff line numberDiff line change
@@ -649,10 +649,12 @@ def astype_nansafe(arr, dtype, copy=True):
649649
if issubclass(dtype.type, text_type):
650650
# in Py3 that's str, in Py2 that's unicode
651651
return lib.astype_unicode(arr.ravel()).reshape(arr.shape)
652+
652653
elif issubclass(dtype.type, string_types):
653654
return lib.astype_str(arr.ravel()).reshape(arr.shape)
655+
654656
elif is_datetime64_dtype(arr):
655-
if dtype == object:
657+
if is_object_dtype(dtype):
656658
return tslib.ints_to_pydatetime(arr.view(np.int64))
657659
elif dtype == np.int64:
658660
return arr.view(dtype)
@@ -666,10 +668,10 @@ def astype_nansafe(arr, dtype, copy=True):
666668
to_dtype=dtype))
667669

668670
elif is_timedelta64_dtype(arr):
669-
if dtype == np.int64:
670-
return arr.view(dtype)
671-
elif dtype == object:
671+
if is_object_dtype(dtype):
672672
return tslib.ints_to_pytimedelta(arr.view(np.int64))
673+
elif dtype == np.int64:
674+
return arr.view(dtype)
673675

674676
# in py3, timedelta64[ns] are int64
675677
if ((PY3 and dtype not in [_INT64_DTYPE, _TD_DTYPE]) or
@@ -696,9 +698,21 @@ def astype_nansafe(arr, dtype, copy=True):
696698
raise ValueError('Cannot convert non-finite values (NA or inf) to '
697699
'integer')
698700

699-
elif is_object_dtype(arr.dtype) and np.issubdtype(dtype.type, np.integer):
701+
elif is_object_dtype(arr):
702+
700703
# work around NumPy brokenness, #1987
701-
return lib.astype_intsafe(arr.ravel(), dtype).reshape(arr.shape)
704+
if np.issubdtype(dtype.type, np.integer):
705+
return lib.astype_intsafe(arr.ravel(), dtype).reshape(arr.shape)
706+
707+
# if we have a datetime/timedelta array of objects
708+
# then coerce to a proper dtype and recall astype_nansafe
709+
710+
elif is_datetime64_dtype(dtype):
711+
from pandas import to_datetime
712+
return astype_nansafe(to_datetime(arr).values, dtype, copy=copy)
713+
elif is_timedelta64_dtype(dtype):
714+
from pandas import to_timedelta
715+
return astype_nansafe(to_timedelta(arr).values, dtype, copy=copy)
702716

703717
if dtype.name in ("datetime64", "timedelta64"):
704718
msg = ("Passing in '{dtype}' dtype with no frequency is "
@@ -709,7 +723,6 @@ def astype_nansafe(arr, dtype, copy=True):
709723
dtype = np.dtype(dtype.name + "[ns]")
710724

711725
if copy:
712-
713726
return arr.astype(dtype, copy=True)
714727
return arr.view(dtype)
715728

pandas/tests/frame/test_dtypes.py

+16
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,22 @@ def test_astype_categoricaldtype_class_raises(self, cls):
640640
with tm.assert_raises_regex(TypeError, xpr):
641641
df['A'].astype(cls)
642642

643+
@pytest.mark.parametrize("dtype", ["M8", "m8"])
644+
@pytest.mark.parametrize("unit", ['ns', 'us', 'ms', 's', 'h', 'm', 'D'])
645+
def test_astype_from_datetimelike_to_objectt(self, dtype, unit):
646+
# tests astype to object dtype
647+
# gh-19223 / gh-12425
648+
dtype = "{}[{}]".format(dtype, unit)
649+
arr = np.array([[1, 2, 3]], dtype=dtype)
650+
df = DataFrame(arr)
651+
result = df.astype(object)
652+
assert (result.dtypes == object).all()
653+
654+
if dtype.startswith('M8'):
655+
assert result.iloc[0, 0] == pd.to_datetime(1, unit=unit)
656+
else:
657+
assert result.iloc[0, 0] == pd.to_timedelta(1, unit=unit)
658+
643659
@pytest.mark.parametrize("arr_dtype", [np.int64, np.float64])
644660
@pytest.mark.parametrize("dtype", ["M8", "m8"])
645661
@pytest.mark.parametrize("unit", ['ns', 'us', 'ms', 's', 'h', 'm', 'D'])

0 commit comments

Comments
 (0)