diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 9fab1d12fc6a5..3ff00f079c347 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -699,6 +699,7 @@ Indexing - Bug in :meth:`Index.get_indexer` and similar methods when ``NaN`` is located at or after position 128 (:issue:`58924`) - Bug in :meth:`MultiIndex.insert` when a new value inserted to a datetime-like level gets cast to ``NaT`` and fails indexing (:issue:`60388`) - Bug in printing :attr:`Index.names` and :attr:`MultiIndex.levels` would not escape single quotes (:issue:`60190`) +- Bug in reindexing of :class:`DataFrame` with :class:`PeriodDtype` columns in case of consolidated block (:issue:`60980`, :issue:`60273`) Missing ^^^^^^^ diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index d1a9081b234de..dc64da35e9725 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -155,12 +155,9 @@ class Block(PandasObject, libinternals.Block): def _validate_ndim(self) -> bool: """ We validate dimension for blocks that can hold 2D values, which for now - means numpy dtypes or DatetimeTZDtype. + means numpy dtypes or EA dtypes like DatetimeTZDtype and PeriodDtype. """ - dtype = self.dtype - return not isinstance(dtype, ExtensionDtype) or isinstance( - dtype, DatetimeTZDtype - ) + return not is_1d_only_ea_dtype(self.dtype) @final @cache_readonly diff --git a/pandas/tests/indexing/test_indexing.py b/pandas/tests/indexing/test_indexing.py index fb7e6649c534f..113eb6c2b2c31 100644 --- a/pandas/tests/indexing/test_indexing.py +++ b/pandas/tests/indexing/test_indexing.py @@ -763,6 +763,27 @@ def test_partial_boolean_frame_indexing(self): ) tm.assert_frame_equal(result, expected) + def test_period_column_slicing(self): + # GH#60273 The transpose operation creates a single 5x1 block of PeriodDtype + # Make sure it is reindexed correctly + df = DataFrame( + pd.period_range("2021-01-01", periods=5, freq="D"), + columns=["A"], + ).T + result = df[[0, 1, 2]] + expected = DataFrame( + [ + [ + pd.Period("2021-01-01", freq="D"), + pd.Period("2021-01-02", freq="D"), + pd.Period("2021-01-03", freq="D"), + ] + ], + index=["A"], + columns=[0, 1, 2], + ) + tm.assert_frame_equal(result, expected) + def test_no_reference_cycle(self): df = DataFrame({"a": [0, 1], "b": [2, 3]}) for name in ("loc", "iloc", "at", "iat"): diff --git a/pandas/tests/internals/test_internals.py b/pandas/tests/internals/test_internals.py index c1f71c6de92dd..ac8ac0766f04d 100644 --- a/pandas/tests/internals/test_internals.py +++ b/pandas/tests/internals/test_internals.py @@ -1320,6 +1320,16 @@ def test_period_can_hold_element(self, element): with pytest.raises(TypeError, match="Invalid value"): self.check_series_setitem(elem, pi, False) + def test_period_reindex_axis(self): + # GH#60273 Test reindexing of block with PeriodDtype + pi = period_range("2020", periods=5, freq="Y") + blk = new_block(pi._data.reshape(5, 1), BlockPlacement(slice(5)), ndim=2) + mgr = BlockManager(blocks=(blk,), axes=[Index(np.arange(5)), Index(["a"])]) + reindexed = mgr.reindex_axis(Index([0, 2, 4]), axis=0) + result = DataFrame._from_mgr(reindexed, axes=reindexed.axes) + expected = DataFrame([[pi[0], pi[2], pi[4]]], columns=[0, 2, 4], index=["a"]) + tm.assert_frame_equal(result, expected) + def check_can_hold_element(self, obj, elem, inplace: bool): blk = obj._mgr.blocks[0] if inplace: