Skip to content

Commit 27f9d05

Browse files
jbrockmendeljreback
authored andcommitted
BUG: fix tzaware dataframe transpose bug (#26825)
1 parent 38ab752 commit 27f9d05

File tree

10 files changed

+207
-97
lines changed

10 files changed

+207
-97
lines changed

doc/source/whatsnew/v0.25.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ Reshaping
784784
- Bug in :func:`DataFrame.sort_index` where an error is thrown when a multi-indexed ``DataFrame`` is sorted on all levels with the initial level sorted last (:issue:`26053`)
785785
- Bug in :meth:`Series.nlargest` treats ``True`` as smaller than ``False`` (:issue:`26154`)
786786
- Bug in :func:`DataFrame.pivot_table` with a :class:`IntervalIndex` as pivot index would raise ``TypeError`` (:issue:`25814`)
787+
- Bug in :meth:`DataFrame.transpose` where transposing a DataFrame with a timezone-aware datetime column would incorrectly raise ``ValueError`` (:issue:`26825`)
787788

788789
Sparse
789790
^^^^^^
@@ -811,6 +812,7 @@ Other
811812
- Removed unused C functions from vendored UltraJSON implementation (:issue:`26198`)
812813
- Allow :class:`Index` and :class:`RangeIndex` to be passed to numpy ``min`` and ``max`` functions (:issue:`26125`)
813814
- Use actual class name in repr of empty objects of a ``Series`` subclass (:issue:`27001`).
815+
- Bug in :class:`DataFrame` where passing an object array of timezone-aware `datetime` objects would incorrectly raise ``ValueError`` (:issue:`13287`)
814816

815817
.. _whatsnew_0.250.contributors:
816818

pandas/core/groupby/generic.py

+37-12
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
from pandas.errors import AbstractMethodError
2222
from pandas.util._decorators import Appender, Substitution
2323

24-
from pandas.core.dtypes.cast import maybe_downcast_to_dtype
24+
from pandas.core.dtypes.cast import (
25+
maybe_convert_objects, maybe_downcast_to_dtype)
2526
from pandas.core.dtypes.common import (
2627
ensure_int64, ensure_platform_int, is_bool, is_datetimelike,
27-
is_integer_dtype, is_interval_dtype, is_numeric_dtype, is_scalar)
28+
is_integer_dtype, is_interval_dtype, is_numeric_dtype, is_object_dtype,
29+
is_scalar)
2830
from pandas.core.dtypes.missing import isna, notna
2931

3032
from pandas._typing import FrameOrSeries
@@ -334,7 +336,6 @@ def _decide_output_index(self, output, labels):
334336

335337
def _wrap_applied_output(self, keys, values, not_indexed_same=False):
336338
from pandas.core.index import _all_indexes_same
337-
from pandas.core.tools.numeric import to_numeric
338339

339340
if len(keys) == 0:
340341
return DataFrame(index=keys)
@@ -406,7 +407,6 @@ def first_not_none(values):
406407
# provide a reduction (Frame -> Series) if groups are
407408
# unique
408409
if self.squeeze:
409-
410410
# assign the name to this series
411411
if singular_series:
412412
values[0].name = keys[0]
@@ -481,14 +481,7 @@ def first_not_none(values):
481481
# as we are stacking can easily have object dtypes here
482482
so = self._selected_obj
483483
if so.ndim == 2 and so.dtypes.apply(is_datetimelike).any():
484-
result = result.apply(
485-
lambda x: to_numeric(x, errors='ignore'))
486-
date_cols = self._selected_obj.select_dtypes(
487-
include=['datetime', 'timedelta']).columns
488-
date_cols = date_cols.intersection(result.columns)
489-
result[date_cols] = (result[date_cols]
490-
._convert(datetime=True,
491-
coerce=True))
484+
result = _recast_datetimelike_result(result)
492485
else:
493486
result = result._convert(datetime=True)
494487

@@ -1710,3 +1703,35 @@ def _normalize_keyword_aggregation(kwargs):
17101703
order.append((column,
17111704
com.get_callable_name(aggfunc) or aggfunc))
17121705
return aggspec, columns, order
1706+
1707+
1708+
def _recast_datetimelike_result(result: DataFrame) -> DataFrame:
1709+
"""
1710+
If we have date/time like in the original, then coerce dates
1711+
as we are stacking can easily have object dtypes here.
1712+
1713+
Parameters
1714+
----------
1715+
result : DataFrame
1716+
1717+
Returns
1718+
-------
1719+
DataFrame
1720+
1721+
Notes
1722+
-----
1723+
- Assumes Groupby._selected_obj has ndim==2 and at least one
1724+
datetimelike column
1725+
"""
1726+
result = result.copy()
1727+
1728+
obj_cols = [idx for idx in range(len(result.columns))
1729+
if is_object_dtype(result.dtypes[idx])]
1730+
1731+
# See GH#26285
1732+
for n in obj_cols:
1733+
converted = maybe_convert_objects(result.iloc[:, n].values,
1734+
convert_numeric=False)
1735+
1736+
result.iloc[:, n] = converted
1737+
return result

pandas/core/internals/construction.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,28 @@ def init_ndarray(values, index, columns, dtype=None, copy=False):
159159
# on the entire block; this is to convert if we have datetimelike's
160160
# embedded in an object type
161161
if dtype is None and is_object_dtype(values):
162-
values = maybe_infer_to_datetimelike(values)
163162

164-
return create_block_manager_from_blocks([values], [columns, index])
163+
if values.ndim == 2 and values.shape[0] != 1:
164+
# transpose and separate blocks
165+
166+
dvals_list = [maybe_infer_to_datetimelike(row) for row in values]
167+
for n in range(len(dvals_list)):
168+
if isinstance(dvals_list[n], np.ndarray):
169+
dvals_list[n] = dvals_list[n].reshape(1, -1)
170+
171+
from pandas.core.internals.blocks import make_block
172+
173+
# TODO: What about re-joining object columns?
174+
block_values = [make_block(dvals_list[n], placement=[n])
175+
for n in range(len(dvals_list))]
176+
177+
else:
178+
datelike_vals = maybe_infer_to_datetimelike(values)
179+
block_values = [datelike_vals]
180+
else:
181+
block_values = [values]
182+
183+
return create_block_manager_from_blocks(block_values, [columns, index])
165184

166185

167186
def init_dict(data, index, columns, dtype=None):

0 commit comments

Comments
 (0)