diff --git a/doc/source/whatsnew/v1.0.1.rst b/doc/source/whatsnew/v1.0.1.rst index 20cfcfbde389c..5ce9b01c266f8 100644 --- a/doc/source/whatsnew/v1.0.1.rst +++ b/doc/source/whatsnew/v1.0.1.rst @@ -24,6 +24,7 @@ Fixed regressions - Fixed regression in :meth:`to_datetime` when parsing non-nanosecond resolution datetimes (:issue:`31491`) - Fixed regression in :meth:`~DataFrame.to_csv` where specifying an ``na_rep`` might truncate the values written (:issue:`31447`) - Fixed regression in :class:`Categorical` construction with ``numpy.str_`` categories (:issue:`31499`) +- Fixed regression in :meth:`DataFrame.loc` and :meth:`DataFrame.iloc` when selecting a row containing a single ``datetime64`` or ``timedelta64`` column (:issue:`31649`) - Fixed regression where setting :attr:`pd.options.display.max_colwidth` was not accepting negative integer. In addition, this behavior has been deprecated in favor of using ``None`` (:issue:`31532`) - Fixed regression in objTOJSON.c fix return-type warning (:issue:`31463`) - Fixed regression in :meth:`qcut` when passed a nullable integer. (:issue:`31389`) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 9e31ccebd0f1b..cb03fbe1770b3 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -7,7 +7,7 @@ import numpy as np -from pandas._libs import NaT, algos as libalgos, lib, tslib, writers +from pandas._libs import NaT, Timestamp, algos as libalgos, lib, tslib, writers from pandas._libs.index import convert_scalar import pandas._libs.internals as libinternals from pandas._libs.tslibs import Timedelta, conversion @@ -2158,6 +2158,16 @@ def internal_values(self): # Override to return DatetimeArray and TimedeltaArray return self.array_values() + def iget(self, key): + # GH#31649 we need to wrap scalars in Timestamp/Timedelta + # TODO: this can be removed if we ever have 2D EA + result = super().iget(key) + if isinstance(result, np.datetime64): + result = Timestamp(result) + elif isinstance(result, np.timedelta64): + result = Timedelta(result) + return result + class DatetimeBlock(DatetimeLikeBlockMixin, Block): __slots__ = () diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 9a01ee18928c0..ca4d1ff067f3d 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -2165,3 +2165,41 @@ def test_set_reset(self): df = result.set_index("foo") tm.assert_index_equal(df.index, idx) + + +def test_object_casting_indexing_wraps_datetimelike(): + # GH#31649, check the indexing methods all the way down the stack + df = pd.DataFrame( + { + "A": [1, 2], + "B": pd.date_range("2000", periods=2), + "C": pd.timedelta_range("1 Day", periods=2), + } + ) + + ser = df.loc[0] + assert isinstance(ser.values[1], pd.Timestamp) + assert isinstance(ser.values[2], pd.Timedelta) + + ser = df.iloc[0] + assert isinstance(ser.values[1], pd.Timestamp) + assert isinstance(ser.values[2], pd.Timedelta) + + ser = df.xs(0, axis=0) + assert isinstance(ser.values[1], pd.Timestamp) + assert isinstance(ser.values[2], pd.Timedelta) + + mgr = df._data + arr = mgr.fast_xs(0) + assert isinstance(arr[1], pd.Timestamp) + assert isinstance(arr[2], pd.Timedelta) + + blk = mgr.blocks[mgr._blknos[1]] + assert blk.dtype == "M8[ns]" # we got the right block + val = blk.iget((0, 0)) + assert isinstance(val, pd.Timestamp) + + blk = mgr.blocks[mgr._blknos[2]] + assert blk.dtype == "m8[ns]" # we got the right block + val = blk.iget((0, 0)) + assert isinstance(val, pd.Timedelta)