diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index a373b57accaa9..6726374dbe30e 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -4,7 +4,6 @@ from __future__ import annotations -from contextlib import suppress from datetime import ( date, datetime, @@ -29,7 +28,6 @@ NaT, OutOfBoundsDatetime, OutOfBoundsTimedelta, - Period, Timedelta, Timestamp, conversion, @@ -87,7 +85,6 @@ PeriodDtype, ) from pandas.core.dtypes.generic import ( - ABCDataFrame, ABCExtensionArray, ABCSeries, ) @@ -249,9 +246,6 @@ def maybe_downcast_to_dtype(result: ArrayLike, dtype: str | np.dtype) -> ArrayLi try to cast to the specified dtype (e.g. convert back to bool/int or could be an astype of float64->float32 """ - if isinstance(result, ABCDataFrame): - # see test_pivot_table_doctest_case - return result do_round = False if isinstance(dtype, str): @@ -278,15 +272,9 @@ def maybe_downcast_to_dtype(result: ArrayLike, dtype: str | np.dtype) -> ArrayLi dtype = np.dtype(dtype) - elif dtype.type is Period: - from pandas.core.arrays import PeriodArray - - with suppress(TypeError): - # e.g. TypeError: int() argument must be a string, a - # bytes-like object or a number, not 'Period - - # error: "dtype[Any]" has no attribute "freq" - return PeriodArray(result, freq=dtype.freq) # type: ignore[attr-defined] + if not isinstance(dtype, np.dtype): + # enforce our signature annotation + raise TypeError(dtype) # pragma: no cover converted = maybe_downcast_numeric(result, dtype, do_round) if converted is not result: @@ -295,15 +283,7 @@ def maybe_downcast_to_dtype(result: ArrayLike, dtype: str | np.dtype) -> ArrayLi # a datetimelike # GH12821, iNaT is cast to float if dtype.kind in ["M", "m"] and result.dtype.kind in ["i", "f"]: - if isinstance(dtype, DatetimeTZDtype): - # convert to datetime and change timezone - i8values = result.astype("i8", copy=False) - cls = dtype.construct_array_type() - # equiv: DatetimeArray(i8values).tz_localize("UTC").tz_convert(dtype.tz) - dt64values = i8values.view("M8[ns]") - result = cls._simple_new(dt64values, dtype=dtype) - else: - result = result.astype(dtype) + result = result.astype(dtype) return result diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 37fc5de95b3d2..38766d2856cfe 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -7213,13 +7213,14 @@ def combine( else: # if we have different dtypes, possibly promote new_dtype = find_common_type([this_dtype, other_dtype]) - if not is_dtype_equal(this_dtype, new_dtype): - series = series.astype(new_dtype) - if not is_dtype_equal(other_dtype, new_dtype): - otherSeries = otherSeries.astype(new_dtype) + series = series.astype(new_dtype, copy=False) + otherSeries = otherSeries.astype(new_dtype, copy=False) arr = func(series, otherSeries) - arr = maybe_downcast_to_dtype(arr, new_dtype) + if isinstance(new_dtype, np.dtype): + # if new_dtype is an EA Dtype, then `func` is expected to return + # the correct dtype without any additional casting + arr = maybe_downcast_to_dtype(arr, new_dtype) result[col] = arr diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 795f5250012cb..ddc6e92b04927 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -174,7 +174,15 @@ def __internal_pivot_table( and v in agged and not is_integer_dtype(agged[v]) ): - agged[v] = maybe_downcast_to_dtype(agged[v], data[v].dtype) + if isinstance(agged[v], ABCDataFrame): + # exclude DataFrame case bc maybe_downcast_to_dtype expects + # ArrayLike + # TODO: why does test_pivot_table_doctest_case fail if + # we don't do this apparently-unnecessary setitem? + agged[v] = agged[v] + pass + else: + agged[v] = maybe_downcast_to_dtype(agged[v], data[v].dtype) table = agged diff --git a/pandas/tests/dtypes/cast/test_downcast.py b/pandas/tests/dtypes/cast/test_downcast.py index 0c3e9841eba3e..5217b38f155c8 100644 --- a/pandas/tests/dtypes/cast/test_downcast.py +++ b/pandas/tests/dtypes/cast/test_downcast.py @@ -5,11 +5,7 @@ from pandas.core.dtypes.cast import maybe_downcast_to_dtype -from pandas import ( - DatetimeIndex, - Series, - Timestamp, -) +from pandas import Series import pandas._testing as tm @@ -77,7 +73,7 @@ def test_downcast_conversion_nan(float_dtype): def test_downcast_conversion_empty(any_real_dtype): dtype = any_real_dtype arr = np.array([], dtype=dtype) - result = maybe_downcast_to_dtype(arr, "int64") + result = maybe_downcast_to_dtype(arr, np.dtype("int64")) tm.assert_numpy_array_equal(result, np.array([], dtype=np.int64)) @@ -89,15 +85,3 @@ def test_datetime_likes_nan(klass): exp = np.array([1, 2, klass("NaT")], dtype) res = maybe_downcast_to_dtype(arr, dtype) tm.assert_numpy_array_equal(res, exp) - - -@pytest.mark.parametrize("as_asi", [True, False]) -def test_datetime_with_timezone(as_asi): - # see gh-15426 - ts = Timestamp("2016-01-01 12:00:00", tz="US/Pacific") - exp = DatetimeIndex([ts, ts])._data - - obj = exp.asi8 if as_asi else exp - res = maybe_downcast_to_dtype(obj, exp.dtype) - - tm.assert_datetime_array_equal(res, exp)