diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index 37e83ddb0ffed..51a947f79ede2 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -716,6 +716,14 @@ cpdef ndarray[object] ensure_string_array( Py_ssize_t i = 0, n = len(arr) if hasattr(arr, "to_numpy"): + + if hasattr(arr, "dtype") and arr.dtype.kind in ["m", "M"]: + # dtype check to exclude DataFrame + # GH#41409 TODO: not a great place for this + out = arr.astype(str).astype(object) + out[arr.isna()] = na_value + return out + arr = arr.to_numpy() elif not isinstance(arr, np.ndarray): arr = np.array(arr, dtype="object") diff --git a/pandas/tests/frame/methods/test_astype.py b/pandas/tests/frame/methods/test_astype.py index 881f8db305240..bd71f0a0716b4 100644 --- a/pandas/tests/frame/methods/test_astype.py +++ b/pandas/tests/frame/methods/test_astype.py @@ -632,13 +632,9 @@ def test_astype_tz_object_conversion(self, tz): result = result.astype({"tz": "datetime64[ns, Europe/London]"}) tm.assert_frame_equal(result, expected) - def test_astype_dt64_to_string(self, frame_or_series, tz_naive_fixture, request): + def test_astype_dt64_to_string(self, frame_or_series, tz_naive_fixture): + # GH#41409 tz = tz_naive_fixture - if tz is None: - mark = pytest.mark.xfail( - reason="GH#36153 uses ndarray formatting instead of DTA formatting" - ) - request.node.add_marker(mark) dti = date_range("2016-01-01", periods=3, tz=tz) dta = dti._data @@ -660,6 +656,15 @@ def test_astype_dt64_to_string(self, frame_or_series, tz_naive_fixture, request) alt = obj.astype(str) assert np.all(alt.iloc[1:] == result.iloc[1:]) + def test_astype_td64_to_string(self, frame_or_series): + # GH#41409 + tdi = pd.timedelta_range("1 Day", periods=3) + obj = frame_or_series(tdi) + + expected = frame_or_series(["1 days", "2 days", "3 days"], dtype="string") + result = obj.astype("string") + tm.assert_equal(result, expected) + def test_astype_bytes(self): # GH#39474 result = DataFrame(["foo", "bar", "baz"]).astype(bytes)