|
101 | 101 | ensure_platform_int,
|
102 | 102 | infer_dtype_from_object,
|
103 | 103 | is_1d_only_ea_dtype,
|
| 104 | + is_1d_only_ea_obj, |
104 | 105 | is_bool_dtype,
|
105 | 106 | is_dataclass,
|
106 | 107 | is_datetime64_any_dtype,
|
|
139 | 140 | )
|
140 | 141 | from pandas.core.array_algos.take import take_2d_multi
|
141 | 142 | from pandas.core.arraylike import OpsMixin
|
142 |
| -from pandas.core.arrays import ExtensionArray |
| 143 | +from pandas.core.arrays import ( |
| 144 | + DatetimeArray, |
| 145 | + ExtensionArray, |
| 146 | + TimedeltaArray, |
| 147 | +) |
143 | 148 | from pandas.core.arrays.sparse import SparseFrameAccessor
|
144 | 149 | from pandas.core.construction import (
|
145 | 150 | extract_array,
|
@@ -852,6 +857,28 @@ def _can_fast_transpose(self) -> bool:
|
852 | 857 | # TODO(EA2D) special case would be unnecessary with 2D EAs
|
853 | 858 | return not is_1d_only_ea_dtype(dtype)
|
854 | 859 |
|
| 860 | + @property |
| 861 | + def _values_compat(self) -> np.ndarray | DatetimeArray | TimedeltaArray: |
| 862 | + """ |
| 863 | + Analogue to ._values that may return a 2D ExtensionArray. |
| 864 | + """ |
| 865 | + mgr = self._mgr |
| 866 | + if isinstance(mgr, ArrayManager): |
| 867 | + return self._values |
| 868 | + |
| 869 | + blocks = mgr.blocks |
| 870 | + if len(blocks) != 1: |
| 871 | + return self._values |
| 872 | + |
| 873 | + arr = blocks[0].values |
| 874 | + if arr.ndim == 1: |
| 875 | + # non-2D ExtensionArray |
| 876 | + return self._values |
| 877 | + |
| 878 | + # more generally, whatever we allow in NDArrayBackedExtensionBlock |
| 879 | + arr = cast("DatetimeArray | TimedeltaArray", arr) |
| 880 | + return arr.T |
| 881 | + |
855 | 882 | # ----------------------------------------------------------------------
|
856 | 883 | # Rendering Methods
|
857 | 884 |
|
@@ -3292,7 +3319,18 @@ def transpose(self, *args, copy: bool = False) -> DataFrame:
|
3292 | 3319 | # construct the args
|
3293 | 3320 |
|
3294 | 3321 | dtypes = list(self.dtypes)
|
3295 |
| - if self._is_homogeneous_type and dtypes and is_extension_array_dtype(dtypes[0]): |
| 3322 | + |
| 3323 | + if self._can_fast_transpose: |
| 3324 | + # Note: tests pass without this, but this improves perf quite a bit. |
| 3325 | + new_vals = self._values_compat.T |
| 3326 | + if copy: |
| 3327 | + new_vals = new_vals.copy() |
| 3328 | + |
| 3329 | + result = self._constructor(new_vals, index=self.columns, columns=self.index) |
| 3330 | + |
| 3331 | + elif ( |
| 3332 | + self._is_homogeneous_type and dtypes and is_extension_array_dtype(dtypes[0]) |
| 3333 | + ): |
3296 | 3334 | # We have EAs with the same dtype. We can preserve that dtype in transpose.
|
3297 | 3335 | dtype = dtypes[0]
|
3298 | 3336 | arr_type = dtype.construct_array_type()
|
@@ -9760,8 +9798,9 @@ def func(values: np.ndarray):
|
9760 | 9798 |
|
9761 | 9799 | def blk_func(values, axis=1):
|
9762 | 9800 | if isinstance(values, ExtensionArray):
|
9763 |
| - if values.ndim == 2: |
9764 |
| - # i.e. DatetimeArray, TimedeltaArray |
| 9801 | + if not is_1d_only_ea_obj(values) and not isinstance( |
| 9802 | + self._mgr, ArrayManager |
| 9803 | + ): |
9765 | 9804 | return values._reduce(name, axis=1, skipna=skipna, **kwds)
|
9766 | 9805 | return values._reduce(name, skipna=skipna, **kwds)
|
9767 | 9806 | else:
|
|
0 commit comments