Skip to content

Commit

Permalink
WIP: rework full/info/expand repr decision tree.
Browse files Browse the repository at this point in the history
  • Loading branch information
lodagro committed Apr 10, 2013
1 parent 35b002a commit 7db1af4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 139 deletions.
3 changes: 3 additions & 0 deletions pandas/core/config_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
111 changes: 54 additions & 57 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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):

This comment has been minimized.

Copy link
@hayd

hayd Apr 13, 2013

Contributor

I think so too (but maybe even just len(self))

This comment has been minimized.

Copy link
@lodagro

lodagro Apr 15, 2013

Author Contributor

The reason the check len(self.columns) < max_rows is there is to avoid start wrapping on a frame where there a so many columns that the expanded frame repr would exceed for sure the max_rows limit. The wrapping/expanding (R-like display) moves columns to rows. See also comment a little bit above.

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
Expand All @@ -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 ('<div style="max-height:1000px;'
'max-width:1500px;overflow:auto;">\n' +
self.to_html() + '\n</div>')
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('<', '&lt').replace('>', '&gt')
return ('<pre>\n' + info + '\n</pre>')
else:
return None

Expand Down
132 changes: 50 additions & 82 deletions pandas/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("<class")

def has_expanded_repr(df):
r = repr(df)
for line in r.split('\n'):
if line.endswith('\\'):
return True
return False


class TestDataFrameFormatting(unittest.TestCase):
_multiprocess_can_split_ = True
Expand Down Expand Up @@ -146,98 +157,55 @@ def test_repr_no_backslash(self):
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))
df_wide = DataFrame('hello', [0], range(10))

def has_info_repr(df):
r = repr(df)
return r.split('\n')[0].startswith("<class")
with option_context('mode.sim_interactive', True):
with option_context('display.width', 50):
with option_context('display.expand_frame_repr', True):
self.assertFalse(has_info_repr(df_small))
self.assertFalse(has_expanded_repr(df_small))
self.assertFalse(has_info_repr(df_wide))
self.assertTrue(has_expanded_repr(df_wide))

def has_wide_repr(df):
r = repr(df)
for line in r.split('\n'):
if line.endswith('\\'):
return True
return False

with option_context('display.line_width', line_width):
with option_context('display.expand_frame_repr', True):
self.assertFalse(has_info_repr(df_small))
self.assertFalse(has_wide_repr(df_small))
self.assertFalse(has_info_repr(df_wide))
self.assertTrue(has_wide_repr(df_wide))
with option_context('display.max_columns', 7):
with option_context('display.expand_frame_repr', False):
self.assertFalse(has_info_repr(df_small))
self.assertFalse(has_wide_repr(df_small))
self.assertFalse(has_expanded_repr(df_small))
self.assertTrue(has_info_repr(df_wide))
self.assertFalse(has_wide_repr(df_wide))

with option_context('display.expand_frame_repr', False):
self.assertFalse(has_info_repr(df_small))
self.assertFalse(has_wide_repr(df_small))
self.assertTrue(has_info_repr(df_wide))
self.assertFalse(has_wide_repr(df_wide))

with option_context('display.line_width', None):
with option_context('display.expand_frame_repr', True):
self.assertFalse(has_info_repr(df_small))
self.assertFalse(has_wide_repr(df_small))
self.assertFalse(has_info_repr(df_wide))
self.assertFalse(has_wide_repr(df_wide))

com.in_interactive_session = original_in_interactive_session
self.assertFalse(has_expanded_repr(df_wide))

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):
def mkframe(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
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()
Expand Down Expand Up @@ -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()

Expand Down

0 comments on commit 7db1af4

Please sign in to comment.