Skip to content

Commit 7db1af4

Browse files
committed
WIP: rework full/info/expand repr decision tree.
1 parent 35b002a commit 7db1af4

File tree

3 files changed

+107
-139
lines changed

3 files changed

+107
-139
lines changed

Diff for: pandas/core/config_init.py

+3
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ def mpl_style_cb(key):
212212
cf.register_option('mpl_style', None, pc_mpl_style_doc,
213213
validator=is_one_of_factory([None, False, 'default']),
214214
cb=mpl_style_cb)
215+
cf.register_option('height', 100, 'TODO', validator=is_int)
216+
cf.register_option('width',80, 'TODO', validator=is_int)
217+
cf.deprecate_option('display.line_width', msg='TODO', rkey='display.width')
215218

216219
tc_sim_interactive_doc = """
217220
: boolean

Diff for: pandas/core/frame.py

+54-57
Original file line numberDiff line numberDiff line change
@@ -599,51 +599,39 @@ def empty(self):
599599
def __nonzero__(self):
600600
raise ValueError("Cannot call bool() on DataFrame.")
601601

602-
def _to_string_max_line_width(self):
603-
buf = StringIO()
604-
self.to_string(buf=buf)
605-
value = buf.getvalue()
606-
return max([len(l) for l in value.split('\n')])
607-
608-
def _need_info_repr_(self):
602+
def _repr_fits_boundaries_(self):
609603
"""
610-
Check if it is needed to use info/summary view to represent a
611-
particular DataFrame.
604+
Check if repr fits in boundaries imposed by the following sets of
605+
display options:
606+
* width, height
607+
* max_rows, max_columns
608+
In case off non-interactive session, no boundaries apply.
612609
"""
613-
if com.in_qtconsole():
614-
terminal_width, terminal_height = 100, 100
615-
else:
616-
terminal_width, terminal_height = get_terminal_size()
617-
max_rows = (terminal_height if get_option("display.max_rows") == 0
618-
else get_option("display.max_rows"))
610+
if not com.in_interactive_session():
611+
return True
612+
613+
terminal_width, terminal_height = get_terminal_size()
614+
615+
# check vertical boundaries (excluding column axis area)
616+
max_rows = get_option("display.max_rows") or terminal_height
617+
display_height = get_option("display.height") or terminal_height
618+
if len(self.index) > min(max_rows, display_height):
619+
return False
620+
621+
# check horizontal boundaries (including index axis area)
619622
max_columns = get_option("display.max_columns")
620-
expand_repr = get_option("display.expand_frame_repr")
621-
line_width = get_option('display.line_width')
623+
display_width = get_option("display.width") or terminal_width
624+
nb_columns = len(self.columns)
625+
if max_columns and nb_columns > max_columns:
626+
return False
627+
if nb_columns > (display_width // 2):
628+
return False
622629

623-
if max_columns > 0:
624-
if ((len(self.index) > max_rows) or
625-
(len(self.columns) > max_columns)):
626-
return True
627-
else:
628-
if expand_repr or (line_width is None):
629-
return False
630-
else:
631-
if len(self.columns) > (line_width // 2):
632-
return True
633-
else:
634-
return self._to_string_max_line_width() > line_width
635-
else:
636-
# save us
637-
if (len(self.index) > max_rows or
638-
(com.in_interactive_session() and
639-
len(self.columns) > terminal_width // 2)):
640-
return True
641-
else:
642-
if (self._to_string_max_line_width() > terminal_width
643-
and com.in_interactive_session()):
644-
return True
645-
else:
646-
return False
630+
buf = StringIO()
631+
self.to_string(buf=buf)
632+
value = buf.getvalue()
633+
repr_width = max([len(l) for l in value.split('\n')])
634+
return repr_width <= display_width
647635

648636
def __str__(self):
649637
"""
@@ -675,26 +663,29 @@ def __unicode__(self):
675663
py2/py3.
676664
"""
677665
buf = StringIO(u"")
678-
if self._need_info_repr_():
679-
max_info_rows = get_option('display.max_info_rows')
680-
verbose = max_info_rows is None or self.shape[0] <= max_info_rows
681-
self.info(buf=buf, verbose=verbose)
666+
if self._repr_fits_boundaries_():
667+
self.to_string(buf=buf)
682668
else:
683-
is_wide = self._need_wide_repr()
684-
line_width = None
685-
if is_wide:
686-
line_width = get_option('display.line_width')
687-
self.to_string(buf=buf, line_width=line_width)
669+
terminal_width, terminal_height = get_terminal_size()
670+
max_rows = get_option("display.max_rows") or terminal_height
671+
# Expand or info? Decide based on option display.expand_frame_repr
672+
# and keep it sane for the number of display rows used by the
673+
# expanded repr.
674+
if (get_option("display.expand_frame_repr") and
675+
len(self.columns) < max_rows):
676+
line_width = get_option("display.width") or terminal_width
677+
self.to_string(buf=buf, line_width=line_width)
678+
else:
679+
max_info_rows = get_option('display.max_info_rows')
680+
verbose = (max_info_rows is None or
681+
self.shape[0] <= max_info_rows)
682+
self.info(buf=buf, verbose=verbose)
688683

689684
value = buf.getvalue()
690685
assert type(value) == unicode
691686

692687
return value
693688

694-
def _need_wide_repr(self):
695-
return (get_option("display.expand_frame_repr")
696-
and com.in_interactive_session())
697-
698689
def __repr__(self):
699690
"""
700691
Return a string representation for a particular DataFrame
@@ -712,12 +703,18 @@ def _repr_html_(self):
712703
raise ValueError('Disable HTML output in QtConsole')
713704

714705
if get_option("display.notebook_repr_html"):
715-
if self._need_info_repr_():
716-
return None
717-
else:
706+
if self._repr_fits_boundaries_():
718707
return ('<div style="max-height:1000px;'
719708
'max-width:1500px;overflow:auto;">\n' +
720709
self.to_html() + '\n</div>')
710+
else:
711+
buf = StringIO(u"")
712+
max_info_rows = get_option('display.max_info_rows')
713+
verbose = (max_info_rows is None or
714+
self.shape[0] <= max_info_rows)
715+
self.info(buf=buf, verbose=verbose)
716+
info = buf.getvalue().replace('<', '&lt').replace('>', '&gt')
717+
return ('<pre>\n' + info + '\n</pre>')
721718
else:
722719
return None
723720

Diff for: pandas/tests/test_format.py

+50-82
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ def curpath():
3232
pth, _ = os.path.split(os.path.abspath(__file__))
3333
return pth
3434

35+
def has_info_repr(df):
36+
r = repr(df)
37+
return r.split('\n')[0].startswith("<class")
38+
39+
def has_expanded_repr(df):
40+
r = repr(df)
41+
for line in r.split('\n'):
42+
if line.endswith('\\'):
43+
return True
44+
return False
45+
3546

3647
class TestDataFrameFormatting(unittest.TestCase):
3748
_multiprocess_can_split_ = True
@@ -146,98 +157,55 @@ def test_repr_no_backslash(self):
146157
self.assertTrue('\\' not in repr(df))
147158

148159
def test_expand_frame_repr(self):
149-
import pandas.core.common as com
150-
original_in_interactive_session = com.in_interactive_session
151-
com.in_interactive_session = lambda: True
152-
line_width = 50
153-
154160
df_small = DataFrame('hello', [0], [0])
155-
df_wide = DataFrame('hello', [0], range(8))
161+
df_wide = DataFrame('hello', [0], range(10))
156162

157-
def has_info_repr(df):
158-
r = repr(df)
159-
return r.split('\n')[0].startswith("<class")
163+
with option_context('mode.sim_interactive', True):
164+
with option_context('display.width', 50):
165+
with option_context('display.expand_frame_repr', True):
166+
self.assertFalse(has_info_repr(df_small))
167+
self.assertFalse(has_expanded_repr(df_small))
168+
self.assertFalse(has_info_repr(df_wide))
169+
self.assertTrue(has_expanded_repr(df_wide))
160170

161-
def has_wide_repr(df):
162-
r = repr(df)
163-
for line in r.split('\n'):
164-
if line.endswith('\\'):
165-
return True
166-
return False
167-
168-
with option_context('display.line_width', line_width):
169-
with option_context('display.expand_frame_repr', True):
170-
self.assertFalse(has_info_repr(df_small))
171-
self.assertFalse(has_wide_repr(df_small))
172-
self.assertFalse(has_info_repr(df_wide))
173-
self.assertTrue(has_wide_repr(df_wide))
174-
with option_context('display.max_columns', 7):
171+
with option_context('display.expand_frame_repr', False):
175172
self.assertFalse(has_info_repr(df_small))
176-
self.assertFalse(has_wide_repr(df_small))
173+
self.assertFalse(has_expanded_repr(df_small))
177174
self.assertTrue(has_info_repr(df_wide))
178-
self.assertFalse(has_wide_repr(df_wide))
179-
180-
with option_context('display.expand_frame_repr', False):
181-
self.assertFalse(has_info_repr(df_small))
182-
self.assertFalse(has_wide_repr(df_small))
183-
self.assertTrue(has_info_repr(df_wide))
184-
self.assertFalse(has_wide_repr(df_wide))
185-
186-
with option_context('display.line_width', None):
187-
with option_context('display.expand_frame_repr', True):
188-
self.assertFalse(has_info_repr(df_small))
189-
self.assertFalse(has_wide_repr(df_small))
190-
self.assertFalse(has_info_repr(df_wide))
191-
self.assertFalse(has_wide_repr(df_wide))
192-
193-
com.in_interactive_session = original_in_interactive_session
175+
self.assertFalse(has_expanded_repr(df_wide))
194176

195177
def test_repr_max_columns_max_rows(self):
196-
import pandas.core.common as com
197-
original_in_interactive_session = com.in_interactive_session
198-
com.in_interactive_session = lambda: True
199-
200178
term_width, term_height = get_terminal_size()
201179
if term_width < 10 or term_height < 10:
202180
raise nose.SkipTest
203181

204-
def repr_is_info_view(n):
182+
def mkframe(n):
205183
index = ['%05d' % i for i in range(n)]
206-
df = DataFrame(0, index, index)
207-
r = repr(df)
208-
nlines = len(r.split('\n'))
209-
return nlines > n + 2
210-
211-
with option_context('display.line_width', term_width * 2):
212-
with option_context('display.max_rows', 5,
213-
'display.max_columns', 5):
214-
self.assertFalse(repr_is_info_view(4))
215-
self.assertFalse(repr_is_info_view(5))
216-
self.assertTrue(repr_is_info_view(6))
217-
218-
with option_context('display.max_rows', 10,
219-
'display.max_columns', 5):
220-
self.assertFalse(repr_is_info_view(5))
221-
self.assertTrue(repr_is_info_view(6))
222-
223-
with option_context('display.max_rows', 5,
224-
'display.max_columns', 10):
225-
self.assertFalse(repr_is_info_view(5))
226-
self.assertTrue(repr_is_info_view(6))
227-
228-
with option_context('display.max_rows', 0,
229-
'display.max_columns', term_height):
230-
self.assertFalse(repr_is_info_view(term_height - 2))
231-
self.assertTrue(repr_is_info_view(term_height + 1))
232-
233-
with option_context('display.max_rows', term_height * 2,
234-
'display.max_columns', 0):
235-
self.assertTrue(com.in_interactive_session())
236-
n = (term_width + 2) // 7
237-
self.assertFalse(repr_is_info_view(n - 1))
238-
self.assertTrue(repr_is_info_view(n + 1))
239-
240-
com.in_interactive_session = original_in_interactive_session
184+
return DataFrame(0, index, index)
185+
186+
with option_context('mode.sim_interactive', True):
187+
with option_context('display.width', term_width * 2):
188+
with option_context('display.max_rows', 5,
189+
'display.max_columns', 5):
190+
self.assertFalse(has_expanded_repr(mkframe(4)))
191+
self.assertFalse(has_expanded_repr(mkframe(5)))
192+
self.assertFalse(has_expanded_repr(mkframe(6)))
193+
self.assertTrue(has_info_repr(mkframe(6)))
194+
195+
with option_context('display.max_rows', 20,
196+
'display.max_columns', 5):
197+
# Out off max_columns boundary, but no extending
198+
# occurs ... can improve?
199+
self.assertFalse(has_expanded_repr(mkframe(6)))
200+
self.assertFalse(has_info_repr(mkframe(6)))
201+
202+
with option_context('display.max_columns', 0,
203+
'display.max_rows', term_width * 20,
204+
'display.width', 0):
205+
df = mkframe((term_width // 7) - 2)
206+
self.assertFalse(has_expanded_repr(df))
207+
df = mkframe((term_width // 7) + 2)
208+
self.assertTrue(has_expanded_repr(df))
241209

242210
def test_to_string_repr_unicode(self):
243211
buf = StringIO()
@@ -1271,8 +1239,8 @@ def get_ipython():
12711239
self.assert_(repstr is not None)
12721240

12731241
fmt.set_printoptions(max_rows=5, max_columns=2)
1274-
1275-
self.assert_(self.frame._repr_html_() is None)
1242+
repstr = self.frame._repr_html_()
1243+
self.assert_('class' in repstr) # info fallback
12761244

12771245
fmt.reset_printoptions()
12781246

0 commit comments

Comments
 (0)