From 6f2aca66beffdd0aef60197f374da7dfe15862f8 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 27 Jun 2018 08:34:43 -0500 Subject: [PATCH 1/4] BUG: quickfix MI repr --- pandas/io/formats/format.py | 2 +- pandas/tests/io/formats/test_format.py | 29 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 12201f62946ac..b2af40adff93d 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -778,7 +778,7 @@ def space_format(x, y): str_columns = list(zip(*[[space_format(x, y) for y in x] for x in fmt_columns])) - if self.sparsify: + if self.sparsify and len(str_columns): str_columns = _sparsify(str_columns) str_columns = [list(x) for x in zip(*str_columns)] diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 63b7cb3459069..35dc66b92758f 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -305,6 +305,35 @@ def test_repr_non_interactive(self): assert not has_truncated_repr(df) assert not has_expanded_repr(df) + def test_repr_multiindex(self): + # https://github.com/pandas-dev/pandas/issues/21180 + # TODO: use mock fixutre. + # This is being backported, so doing it directly here. + try: + from unittest import mock + except ImportError: + mock = pytest.importorskip("mock") + + terminal_size = os.terminal_size((118, 96)) + p1 = mock.patch('pandas.io.formats.console.get_terminal_size', + return_value=terminal_size) + p2 = mock.patch('pandas.io.formats.format.get_terminal_size', + return_value=terminal_size) + + index = range(5) + columns = pd.MultiIndex.from_tuples([ + ('This is a long title with > 37 chars.', 'cat'), + ('This is a loooooonger title with > 43 chars.', 'dog'), + ]) + df = pd.DataFrame(1, index=index, columns=columns) + + with p1, p2: + result = repr(df.head()) + + # This assert will hopefully be replaced by something better in + # the future + assert result.split('\n')[0][-3:] == '...' + def test_repr_max_columns_max_rows(self): term_width, term_height = get_terminal_size() if term_width < 10 or term_height < 10: From 81512937969e65696fecfb2603cbfedc96b2e54a Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 27 Jun 2018 09:06:00 -0500 Subject: [PATCH 2/4] Better fallback --- doc/source/whatsnew/v0.23.2.txt | 2 +- pandas/io/formats/format.py | 2 ++ pandas/tests/io/formats/test_format.py | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.23.2.txt b/doc/source/whatsnew/v0.23.2.txt index 9c4b408a1d24b..075c279dfbab9 100644 --- a/doc/source/whatsnew/v0.23.2.txt +++ b/doc/source/whatsnew/v0.23.2.txt @@ -54,7 +54,7 @@ Fixed Regressions - Fixed regression in :meth:`to_csv` when handling file-like object incorrectly (:issue:`21471`) - Bug in both :meth:`DataFrame.first_valid_index` and :meth:`Series.first_valid_index` raised for a row index having duplicate values (:issue:`21441`) -- +- Fixed printing of DataFrames with hierarchical columns with long names (:issue:`21180`) .. _whatsnew_0232.performance: diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index b2af40adff93d..5a7b5263f07a5 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -640,6 +640,8 @@ def to_string(self): col_lens = col_lens.drop(mid_ix) n_cols = len(col_lens) max_cols_adj = n_cols - self.index # subtract index column + # GH-21180. Ensure that we print at least two. + max_cols_adj = max(max_cols_adj, 2) self.max_cols_adj = max_cols_adj # Call again _chk_truncate to cut frame appropriately diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 35dc66b92758f..c3cfa1b6e8137 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -305,7 +305,7 @@ def test_repr_non_interactive(self): assert not has_truncated_repr(df) assert not has_expanded_repr(df) - def test_repr_multiindex(self): + def test_repr_truncates_terminal_size(self): # https://github.com/pandas-dev/pandas/issues/21180 # TODO: use mock fixutre. # This is being backported, so doing it directly here. @@ -328,11 +328,20 @@ def test_repr_multiindex(self): df = pd.DataFrame(1, index=index, columns=columns) with p1, p2: - result = repr(df.head()) + result = repr(df) + + h1, h2 = result.split('\n')[:2] + assert 'long' in h1 + assert 'loooooonger' in h1 + assert 'cat' in h2 + assert 'dog' in h2 + + # regular columns + df2 = pd.DataFrame({"A" * 41: [1, 2], 'B' * 41: [1, 2]}) + with p1, p2: + result = repr(df2) - # This assert will hopefully be replaced by something better in - # the future - assert result.split('\n')[0][-3:] == '...' + assert df2.columns[0] in result.split('\n')[0] def test_repr_max_columns_max_rows(self): term_width, term_height = get_terminal_size() From 4e11634c35982b20acec35fb1ddd1cd1f2612fb8 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 27 Jun 2018 12:52:26 -0500 Subject: [PATCH 3/4] Py2 compat --- pandas/tests/io/formats/test_format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index c3cfa1b6e8137..191e3f37f1c37 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -314,7 +314,7 @@ def test_repr_truncates_terminal_size(self): except ImportError: mock = pytest.importorskip("mock") - terminal_size = os.terminal_size((118, 96)) + terminal_size = (118, 96) p1 = mock.patch('pandas.io.formats.console.get_terminal_size', return_value=terminal_size) p2 = mock.patch('pandas.io.formats.format.get_terminal_size', From 28783583b9c5218ffe42fff74c851fde06a2beb4 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Fri, 29 Jun 2018 05:56:12 -0500 Subject: [PATCH 4/4] update --- pandas/io/formats/format.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 5a7b5263f07a5..c46f4b5ad9c18 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -636,10 +636,12 @@ def to_string(self): mid = int(round(n_cols / 2.)) mid_ix = col_lens.index[mid] col_len = col_lens[mid_ix] - adj_dif -= (col_len + 1) # adjoin adds one + # adjoin adds one + adj_dif -= (col_len + 1) col_lens = col_lens.drop(mid_ix) n_cols = len(col_lens) - max_cols_adj = n_cols - self.index # subtract index column + # subtract index column + max_cols_adj = n_cols - self.index # GH-21180. Ensure that we print at least two. max_cols_adj = max(max_cols_adj, 2) self.max_cols_adj = max_cols_adj