Repository: pandas-dev/pandas

Issue:
BUG: NonConsolidatebleBlock with ndim 1 'take_nd' with len 1 gives wrong ndim
When doing an indexing operation on an EA column that results in a single row, the resulting SingleBlockManager / Block has an inconsistent internal state:

```
In [62]: df = pd.DataFrame({'a': pd.Series([1, 2, 3], dtype='Int64'), 'b': [1, 2, 3]})                                                                        

In [63]: df.loc[[0], 'a']._data                                                                                                                               
Out[63]: 
SingleBlockManager
Items: Int64Index([0], dtype='int64')
ExtensionBlock: slice(0, 1, 1), 1 x 1, dtype: Int64

In [64]: df.loc[[0], 'a']._data.ndim                                                                                                                          
Out[64]: 1

In [65]: df.loc[[0], 'a']._data._block.ndim                                                                                                                   
Out[65]: 2

In [66]: df.loc[[0], 'b']._data                                                                                                                               
Out[66]: 
SingleBlockManager
Items: Int64Index([0], dtype='int64')
IntBlock: 1 dtype: int64

In [67]: df.loc[[0], 'b']._data._block.ndim                                                                                                                   
Out[67]: 1
```

So `df.loc[[0], 'a']` gives a Series as result that holds a 2d block, which leads to other bugs (although not directly visible in the repr. Example: https://github.com/geopandas/geopandas/issues/1078)

The reason for this is that `Block.take_nd` does not specify the ndim of the resulting new Block, and thus this is inferred in the `NonConsolidatableBlock.__init__`:

https://github.com/pandas-dev/pandas/blob/640d9e1f5fe8ab64d1f6496b8216c28185e53225/pandas/core/internals/blocks.py#L1593-L1598

But, this `len(placement)` is not only 1 in case of a 2D block, but also if you have a 1D block with values of len 1 ...

This bug shows with any EA, so also eg Categorical. And it seems to go back to pandas 0.23, and still manifests on master.



Discussion:

Resolving patch:
diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py
--- a/pandas/core/internals/blocks.py
+++ b/pandas/core/internals/blocks.py
@@ -273,6 +273,8 @@ def make_block_same_class(self, values, placement=None, ndim=None, dtype=None):
             )
         if placement is None:
             placement = self.mgr_locs
+        if ndim is None:
+            ndim = self.ndim
         return make_block(
             values, placement=placement, ndim=ndim, klass=self.__class__, dtype=dtype
         )


Test patch:
diff --git a/pandas/tests/extension/base/getitem.py b/pandas/tests/extension/base/getitem.py
--- a/pandas/tests/extension/base/getitem.py
+++ b/pandas/tests/extension/base/getitem.py
@@ -260,3 +260,9 @@ def test_reindex_non_na_fill_value(self, data_missing):
         expected = pd.Series(data_missing._from_sequence([na, valid, valid]))
 
         self.assert_series_equal(result, expected)
+
+    def test_loc_len1(self, data):
+        # see GH-27785 take_nd with indexer of len 1 resulting in wrong ndim
+        df = pd.DataFrame({"A": data})
+        res = df.loc[[0], "A"]
+        assert res._data._block.ndim == 1