diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 823bfc75e4304..7b5f1fb03dd48 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -733,8 +733,8 @@ Sparse ExtensionArray ^^^^^^^^^^^^^^ -- Fixed bug where :meth:`Series.value_counts` would raise on empty input of ``Int64`` dtype (:issue:`33317`) -- +- Fixed bug where :meth:`Serires.value_counts` would raise on empty input of ``Int64`` dtype (:issue:`33317`) +- Fixed bug that caused :meth:`Series.__repr__()` to crash for extension types whose elements are multidimensional arrays (:issue:`33770`). Other diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 59542a8da535e..c7eb3eeedcadf 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1228,7 +1228,11 @@ def _format(x): vals = extract_array(self.values, extract_numpy=True) - is_float_type = lib.map_infer(vals, is_float) & notna(vals) + is_float_type = ( + lib.map_infer(vals, is_float) + # vals may have 2 or more dimensions + & np.all(notna(vals), axis=tuple(range(1, len(vals.shape)))) + ) leading_space = self.leading_space if leading_space is None: leading_space = is_float_type.any() diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index f3c3344992942..c1850826926d8 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -2810,6 +2810,63 @@ def test_to_string_multindex_header(self): assert res == exp +class TestGenericArrayFormatter: + def test_1d_array(self): + # GenericArrayFormatter is used on types for which there isn't a dedicated + # formatter. np.bool is one of those types. + obj = fmt.GenericArrayFormatter(np.array([True, False])) + res = obj.get_result() + assert len(res) == 2 + # Results should be right-justified. + assert res[0] == " True" + assert res[1] == " False" + + def test_2d_array(self): + obj = fmt.GenericArrayFormatter(np.array([[True, False], [False, True]])) + res = obj.get_result() + assert len(res) == 2 + assert res[0] == " [True, False]" + assert res[1] == " [False, True]" + + def test_3d_array(self): + obj = fmt.GenericArrayFormatter( + np.array([[[True, True], [False, False]], [[False, True], [True, False]]]) + ) + res = obj.get_result() + assert len(res) == 2 + assert res[0] == " [[True, True], [False, False]]" + assert res[1] == " [[False, True], [True, False]]" + + def test_2d_extension_type(self): + # GH 33770 + + # Define a stub extension type with just enough code to run Series.__repr__() + class DtypeStub(pd.api.extensions.ExtensionDtype): + @property + def type(self): + return np.ndarray + + @property + def name(self): + return "DtypeStub" + + class ExtTypeStub(pd.api.extensions.ExtensionArray): + def __len__(self): + return 2 + + def __getitem__(self, ix): + return [ix == 1, ix == 0] + + @property + def dtype(self): + return DtypeStub() + + series = pd.Series(ExtTypeStub()) + res = repr(series) # This line crashed before #33770 was fixed. + expected = "0 [False True]\n" + "1 [ True False]\n" + "dtype: DtypeStub" + assert res == expected + + def _three_digit_exp(): return f"{1.7e8:.4g}" == "1.7e+008"