From 745408ff63e83bfc73aa18ee4b972af20f40e515 Mon Sep 17 00:00:00 2001 From: Wouter Overmeire Date: Sat, 16 Feb 2013 15:15:14 +0100 Subject: [PATCH 1/5] BUG: fix max_columns=0, close #2856 --- pandas/core/frame.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 091b9926500d9..b9f1b2c13a50e 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -604,6 +604,8 @@ def _need_info_repr_(self): Check if it is needed to use info/summary view to represent a particular DataFrame. """ + if not get_option("display.expand_frame_repr"): + return True if com.in_qtconsole(): terminal_width, terminal_height = 100, 100 @@ -612,11 +614,10 @@ def _need_info_repr_(self): max_rows = (terminal_height if get_option("display.max_rows") == 0 else get_option("display.max_rows")) max_columns = get_option("display.max_columns") - expand_repr = get_option("display.expand_frame_repr") if max_columns > 0: if (len(self.index) <= max_rows and - (len(self.columns) <= max_columns and expand_repr)): + (len(self.columns) <= max_columns)): return False else: return True @@ -624,16 +625,14 @@ def _need_info_repr_(self): # save us if (len(self.index) > max_rows or (com.in_interactive_session() and - len(self.columns) > terminal_width // 2 and - not expand_repr)): + len(self.columns) > terminal_width // 2)): return True else: buf = StringIO() self.to_string(buf=buf) value = buf.getvalue() if (max([len(l) for l in value.split('\n')]) > terminal_width - and com.in_interactive_session() - and not expand_repr): + and com.in_interactive_session()): return True else: return False From 0da44db37d52b42a1b38deb91346d8eed0967c2f Mon Sep 17 00:00:00 2001 From: Wouter Overmeire Date: Mon, 25 Mar 2013 22:13:15 +0100 Subject: [PATCH 2/5] TST: add test for diplay options max_rows and max_columns --- pandas/tests/test_format.py | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pandas/tests/test_format.py b/pandas/tests/test_format.py index cb55061ae7c3e..344ff74270669 100644 --- a/pandas/tests/test_format.py +++ b/pandas/tests/test_format.py @@ -19,6 +19,7 @@ from pandas.util.py3compat import lzip import pandas.core.format as fmt import pandas.util.testing as tm +from pandas.util.terminal import get_terminal_size import pandas import pandas as pd from pandas.core.config import (set_option, get_option, @@ -144,6 +145,53 @@ def test_repr_no_backslash(self): df = DataFrame(np.random.randn(10, 4)) self.assertTrue('\\' not in repr(df)) + def test_repr_max_columns_max_rows(self): + import pandas.core.common as com + original_in_interactive_session = com.in_interactive_session + com.in_interactive_session = lambda: True + + term_width, term_height = get_terminal_size() + if term_width < 10 or term_height < 10: + raise nose.SkipTest + + def repr_is_info_view(n): + index = ['%05d' % i for i in range(n)] + df = DataFrame(0, index, index) + r = repr(df) + nlines = len(r.split('\n')) + return nlines > n + 2 + + with option_context('display.line_width', term_width * 2): + with option_context('display.max_rows', 5, + 'display.max_columns', 5): + self.assertFalse(repr_is_info_view(4)) + self.assertFalse(repr_is_info_view(5)) + self.assertTrue(repr_is_info_view(6)) + + with option_context('display.max_rows', 10, + 'display.max_columns', 5): + self.assertFalse(repr_is_info_view(5)) + self.assertTrue(repr_is_info_view(6)) + + with option_context('display.max_rows', 5, + 'display.max_columns', 10): + self.assertFalse(repr_is_info_view(5)) + self.assertTrue(repr_is_info_view(6)) + + with option_context('display.max_rows', 0, + 'display.max_columns', term_height): + self.assertFalse(repr_is_info_view(term_height - 2)) + self.assertTrue(repr_is_info_view(term_height + 1)) + + with option_context('display.max_rows', term_height * 2, + 'display.max_columns', 0): + self.assertTrue(com.in_interactive_session()) + n = (term_width + 2) // 7 + self.assertFalse(repr_is_info_view(n - 1)) + self.assertTrue(repr_is_info_view(n + 1)) + + com.in_interactive_session = original_in_interactive_session + def test_to_string_repr_unicode(self): buf = StringIO() From e479948c7af91752cdf7aacc297fed01ba6fc5a0 Mon Sep 17 00:00:00 2001 From: Wouter Overmeire Date: Wed, 27 Mar 2013 22:31:02 +0100 Subject: [PATCH 3/5] BUG: fix expand_frame_repr --- pandas/core/frame.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index b9f1b2c13a50e..01d8097d5b43f 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -599,14 +599,17 @@ def empty(self): def __nonzero__(self): raise ValueError("Cannot call bool() on DataFrame.") + def _to_string_max_line_width(self): + buf = StringIO() + self.to_string(buf=buf) + value = buf.getvalue() + return max([len(l) for l in value.split('\n')]) + def _need_info_repr_(self): """ Check if it is needed to use info/summary view to represent a particular DataFrame. """ - if not get_option("display.expand_frame_repr"): - return True - if com.in_qtconsole(): terminal_width, terminal_height = 100, 100 else: @@ -614,13 +617,21 @@ def _need_info_repr_(self): max_rows = (terminal_height if get_option("display.max_rows") == 0 else get_option("display.max_rows")) max_columns = get_option("display.max_columns") + expand_repr = get_option("display.expand_frame_repr") + line_width = get_option('display.line_width') if max_columns > 0: - if (len(self.index) <= max_rows and - (len(self.columns) <= max_columns)): - return False - else: + if ((len(self.index) > max_rows) or + (len(self.columns) > max_columns)): return True + else: + if expand_repr or (line_width is None): + return False + else: + if len(self.columns) > (line_width // 2): + return True + else: + return self._to_string_max_line_width() > line_width else: # save us if (len(self.index) > max_rows or @@ -628,10 +639,7 @@ def _need_info_repr_(self): len(self.columns) > terminal_width // 2)): return True else: - buf = StringIO() - self.to_string(buf=buf) - value = buf.getvalue() - if (max([len(l) for l in value.split('\n')]) > terminal_width + if (self._to_string_max_line_width() > terminal_width and com.in_interactive_session()): return True else: From 35b002aba08b0ca64e4f4a06c94bf699c7706820 Mon Sep 17 00:00:00 2001 From: Wouter Overmeire Date: Wed, 27 Mar 2013 22:31:32 +0100 Subject: [PATCH 4/5] TST: add test for display option expand_frame_repr --- pandas/tests/test_format.py | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/pandas/tests/test_format.py b/pandas/tests/test_format.py index 344ff74270669..3d4ebc8c359e4 100644 --- a/pandas/tests/test_format.py +++ b/pandas/tests/test_format.py @@ -145,6 +145,53 @@ def test_repr_no_backslash(self): df = DataFrame(np.random.randn(10, 4)) self.assertTrue('\\' not in repr(df)) + def test_expand_frame_repr(self): + import pandas.core.common as com + original_in_interactive_session = com.in_interactive_session + com.in_interactive_session = lambda: True + line_width = 50 + + df_small = DataFrame('hello', [0], [0]) + df_wide = DataFrame('hello', [0], range(8)) + + def has_info_repr(df): + r = repr(df) + return r.split('\n')[0].startswith(" Date: Wed, 10 Apr 2013 23:31:27 +0200 Subject: [PATCH 5/5] WIP: rework full/info/expand repr decision tree. --- pandas/core/config_init.py | 3 + pandas/core/frame.py | 111 +++++++++++++++--------------- pandas/tests/test_format.py | 132 ++++++++++++++---------------------- 3 files changed, 107 insertions(+), 139 deletions(-) diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index 70f3fb045376e..9f599ffe908ba 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -212,6 +212,9 @@ def mpl_style_cb(key): cf.register_option('mpl_style', None, pc_mpl_style_doc, validator=is_one_of_factory([None, False, 'default']), cb=mpl_style_cb) + cf.register_option('height', 100, 'TODO', validator=is_int) + cf.register_option('width',80, 'TODO', validator=is_int) +cf.deprecate_option('display.line_width', msg='TODO', rkey='display.width') tc_sim_interactive_doc = """ : boolean diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 01d8097d5b43f..ecc35a3ee9749 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -599,51 +599,39 @@ def empty(self): def __nonzero__(self): raise ValueError("Cannot call bool() on DataFrame.") - def _to_string_max_line_width(self): - buf = StringIO() - self.to_string(buf=buf) - value = buf.getvalue() - return max([len(l) for l in value.split('\n')]) - - def _need_info_repr_(self): + def _repr_fits_boundaries_(self): """ - Check if it is needed to use info/summary view to represent a - particular DataFrame. + Check if repr fits in boundaries imposed by the following sets of + display options: + * width, height + * max_rows, max_columns + In case off non-interactive session, no boundaries apply. """ - if com.in_qtconsole(): - terminal_width, terminal_height = 100, 100 - else: - terminal_width, terminal_height = get_terminal_size() - max_rows = (terminal_height if get_option("display.max_rows") == 0 - else get_option("display.max_rows")) + if not com.in_interactive_session(): + return True + + terminal_width, terminal_height = get_terminal_size() + + # check vertical boundaries (excluding column axis area) + max_rows = get_option("display.max_rows") or terminal_height + display_height = get_option("display.height") or terminal_height + if len(self.index) > min(max_rows, display_height): + return False + + # check horizontal boundaries (including index axis area) max_columns = get_option("display.max_columns") - expand_repr = get_option("display.expand_frame_repr") - line_width = get_option('display.line_width') + display_width = get_option("display.width") or terminal_width + nb_columns = len(self.columns) + if max_columns and nb_columns > max_columns: + return False + if nb_columns > (display_width // 2): + return False - if max_columns > 0: - if ((len(self.index) > max_rows) or - (len(self.columns) > max_columns)): - return True - else: - if expand_repr or (line_width is None): - return False - else: - if len(self.columns) > (line_width // 2): - return True - else: - return self._to_string_max_line_width() > line_width - else: - # save us - if (len(self.index) > max_rows or - (com.in_interactive_session() and - len(self.columns) > terminal_width // 2)): - return True - else: - if (self._to_string_max_line_width() > terminal_width - and com.in_interactive_session()): - return True - else: - return False + buf = StringIO() + self.to_string(buf=buf) + value = buf.getvalue() + repr_width = max([len(l) for l in value.split('\n')]) + return repr_width <= display_width def __str__(self): """ @@ -675,26 +663,29 @@ def __unicode__(self): py2/py3. """ buf = StringIO(u"") - if self._need_info_repr_(): - max_info_rows = get_option('display.max_info_rows') - verbose = max_info_rows is None or self.shape[0] <= max_info_rows - self.info(buf=buf, verbose=verbose) + if self._repr_fits_boundaries_(): + self.to_string(buf=buf) else: - is_wide = self._need_wide_repr() - line_width = None - if is_wide: - line_width = get_option('display.line_width') - self.to_string(buf=buf, line_width=line_width) + terminal_width, terminal_height = get_terminal_size() + max_rows = get_option("display.max_rows") or terminal_height + # Expand or info? Decide based on option display.expand_frame_repr + # and keep it sane for the number of display rows used by the + # expanded repr. + if (get_option("display.expand_frame_repr") and + len(self.columns) < max_rows): + line_width = get_option("display.width") or terminal_width + self.to_string(buf=buf, line_width=line_width) + else: + max_info_rows = get_option('display.max_info_rows') + verbose = (max_info_rows is None or + self.shape[0] <= max_info_rows) + self.info(buf=buf, verbose=verbose) value = buf.getvalue() assert type(value) == unicode return value - def _need_wide_repr(self): - return (get_option("display.expand_frame_repr") - and com.in_interactive_session()) - def __repr__(self): """ Return a string representation for a particular DataFrame @@ -712,12 +703,18 @@ def _repr_html_(self): raise ValueError('Disable HTML output in QtConsole') if get_option("display.notebook_repr_html"): - if self._need_info_repr_(): - return None - else: + if self._repr_fits_boundaries_(): return ('
\n' + self.to_html() + '\n
') + else: + buf = StringIO(u"") + max_info_rows = get_option('display.max_info_rows') + verbose = (max_info_rows is None or + self.shape[0] <= max_info_rows) + self.info(buf=buf, verbose=verbose) + info = buf.getvalue().replace('<', '<').replace('>', '>') + return ('
\n' + info + '\n
') else: return None diff --git a/pandas/tests/test_format.py b/pandas/tests/test_format.py index 3d4ebc8c359e4..d1e5c64afe794 100644 --- a/pandas/tests/test_format.py +++ b/pandas/tests/test_format.py @@ -32,6 +32,17 @@ def curpath(): pth, _ = os.path.split(os.path.abspath(__file__)) return pth +def has_info_repr(df): + r = repr(df) + return r.split('\n')[0].startswith(" n + 2 - - with option_context('display.line_width', term_width * 2): - with option_context('display.max_rows', 5, - 'display.max_columns', 5): - self.assertFalse(repr_is_info_view(4)) - self.assertFalse(repr_is_info_view(5)) - self.assertTrue(repr_is_info_view(6)) - - with option_context('display.max_rows', 10, - 'display.max_columns', 5): - self.assertFalse(repr_is_info_view(5)) - self.assertTrue(repr_is_info_view(6)) - - with option_context('display.max_rows', 5, - 'display.max_columns', 10): - self.assertFalse(repr_is_info_view(5)) - self.assertTrue(repr_is_info_view(6)) - - with option_context('display.max_rows', 0, - 'display.max_columns', term_height): - self.assertFalse(repr_is_info_view(term_height - 2)) - self.assertTrue(repr_is_info_view(term_height + 1)) - - with option_context('display.max_rows', term_height * 2, - 'display.max_columns', 0): - self.assertTrue(com.in_interactive_session()) - n = (term_width + 2) // 7 - self.assertFalse(repr_is_info_view(n - 1)) - self.assertTrue(repr_is_info_view(n + 1)) - - com.in_interactive_session = original_in_interactive_session + return DataFrame(0, index, index) + + with option_context('mode.sim_interactive', True): + with option_context('display.width', term_width * 2): + with option_context('display.max_rows', 5, + 'display.max_columns', 5): + self.assertFalse(has_expanded_repr(mkframe(4))) + self.assertFalse(has_expanded_repr(mkframe(5))) + self.assertFalse(has_expanded_repr(mkframe(6))) + self.assertTrue(has_info_repr(mkframe(6))) + + with option_context('display.max_rows', 20, + 'display.max_columns', 5): + # Out off max_columns boundary, but no extending + # occurs ... can improve? + self.assertFalse(has_expanded_repr(mkframe(6))) + self.assertFalse(has_info_repr(mkframe(6))) + + with option_context('display.max_columns', 0, + 'display.max_rows', term_width * 20, + 'display.width', 0): + df = mkframe((term_width // 7) - 2) + self.assertFalse(has_expanded_repr(df)) + df = mkframe((term_width // 7) + 2) + self.assertTrue(has_expanded_repr(df)) def test_to_string_repr_unicode(self): buf = StringIO() @@ -1271,8 +1239,8 @@ def get_ipython(): self.assert_(repstr is not None) fmt.set_printoptions(max_rows=5, max_columns=2) - - self.assert_(self.frame._repr_html_() is None) + repstr = self.frame._repr_html_() + self.assert_('class' in repstr) # info fallback fmt.reset_printoptions()