Skip to content

Commit 8f92d9a

Browse files
committed
Merge PR #2881 and fix unit test
2 parents ba774f5 + 7db1af4 commit 8f92d9a

File tree

3 files changed

+138
-62
lines changed

3 files changed

+138
-62
lines changed

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

pandas/core/frame.py

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

602-
def _need_info_repr_(self):
602+
def _repr_fits_boundaries_(self):
603603
"""
604-
Check if it is needed to use info/summary view to represent a
605-
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.
606609
"""
610+
if not com.in_interactive_session():
611+
return True
607612

608-
if com.in_qtconsole():
609-
terminal_width, terminal_height = 100, 100
610-
else:
611-
terminal_width, terminal_height = get_terminal_size()
612-
max_rows = (terminal_height if get_option("display.max_rows") == 0
613-
else get_option("display.max_rows"))
614-
max_columns = get_option("display.max_columns")
615-
expand_repr = get_option("display.expand_frame_repr")
613+
terminal_width, terminal_height = get_terminal_size()
616614

617-
if max_columns > 0:
618-
if (len(self.index) <= max_rows and
619-
(len(self.columns) <= max_columns and expand_repr)):
620-
return False
621-
else:
622-
return True
623-
else:
624-
# save us
625-
if (len(self.index) > max_rows or
626-
(com.in_interactive_session() and
627-
len(self.columns) > terminal_width // 2 and
628-
not expand_repr)):
629-
return True
630-
else:
631-
buf = StringIO()
632-
self.to_string(buf=buf)
633-
value = buf.getvalue()
634-
if (max([len(l) for l in value.split('\n')]) > terminal_width
635-
and com.in_interactive_session()
636-
and not expand_repr):
637-
return True
638-
else:
639-
return False
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)
622+
max_columns = get_option("display.max_columns")
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
629+
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
640635

641636
def __str__(self):
642637
"""
@@ -668,26 +663,29 @@ def __unicode__(self):
668663
py2/py3.
669664
"""
670665
buf = StringIO(u"")
671-
if self._need_info_repr_():
672-
max_info_rows = get_option('display.max_info_rows')
673-
verbose = max_info_rows is None or self.shape[0] <= max_info_rows
674-
self.info(buf=buf, verbose=verbose)
666+
if self._repr_fits_boundaries_():
667+
self.to_string(buf=buf)
675668
else:
676-
is_wide = self._need_wide_repr()
677-
line_width = None
678-
if is_wide:
679-
line_width = get_option('display.line_width')
680-
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)
681683

682684
value = buf.getvalue()
683685
assert type(value) == unicode
684686

685687
return value
686688

687-
def _need_wide_repr(self):
688-
return (get_option("display.expand_frame_repr")
689-
and com.in_interactive_session())
690-
691689
def __repr__(self):
692690
"""
693691
Return a string representation for a particular DataFrame
@@ -705,12 +703,18 @@ def _repr_html_(self):
705703
raise ValueError('Disable HTML output in QtConsole')
706704

707705
if get_option("display.notebook_repr_html"):
708-
if self._need_info_repr_():
709-
return None
710-
else:
706+
if self._repr_fits_boundaries_():
711707
return ('<div style="max-height:1000px;'
712708
'max-width:1500px;overflow:auto;">\n' +
713709
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>')
714718
else:
715719
return None
716720

pandas/tests/test_format.py

+81-12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from pandas.util.py3compat import lzip
2020
import pandas.core.format as fmt
2121
import pandas.util.testing as tm
22+
from pandas.util.terminal import get_terminal_size
2223
import pandas
2324
import pandas as pd
2425
from pandas.core.config import (set_option, get_option,
@@ -27,6 +28,22 @@
2728
_frame = DataFrame(tm.getSeriesData())
2829

2930

31+
def curpath():
32+
pth, _ = os.path.split(os.path.abspath(__file__))
33+
return pth
34+
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+
46+
3047
class TestDataFrameFormatting(unittest.TestCase):
3148
_multiprocess_can_split_ = True
3249

@@ -139,6 +156,57 @@ def test_repr_no_backslash(self):
139156
df = DataFrame(np.random.randn(10, 4))
140157
self.assertTrue('\\' not in repr(df))
141158

159+
def test_expand_frame_repr(self):
160+
df_small = DataFrame('hello', [0], [0])
161+
df_wide = DataFrame('hello', [0], range(10))
162+
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))
170+
171+
with option_context('display.expand_frame_repr', False):
172+
self.assertFalse(has_info_repr(df_small))
173+
self.assertFalse(has_expanded_repr(df_small))
174+
self.assertTrue(has_info_repr(df_wide))
175+
self.assertFalse(has_expanded_repr(df_wide))
176+
177+
def test_repr_max_columns_max_rows(self):
178+
term_width, term_height = get_terminal_size()
179+
if term_width < 10 or term_height < 10:
180+
raise nose.SkipTest
181+
182+
def mkframe(n):
183+
index = ['%05d' % i for i in range(n)]
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))
209+
142210
def test_to_string_repr_unicode(self):
143211
buf = StringIO()
144212

@@ -525,19 +593,20 @@ def test_setting(value, nrows=3, ncols=2):
525593
else:
526594
raise ValueError("'value' must be int or None")
527595

528-
pd.set_option('display.max_rows', nrows - 1)
529-
pd.set_option('display.max_info_rows', value)
596+
with option_context('mode.sim_interactive', True):
597+
pd.set_option('display.max_rows', nrows - 1)
598+
pd.set_option('display.max_info_rows', value)
530599

531-
smallx = DataFrame(np.random.rand(nrows, ncols))
532-
repr_small = repr(smallx)
600+
smallx = DataFrame(np.random.rand(nrows, ncols))
601+
repr_small = repr(smallx)
533602

534-
bigx = DataFrame(np.random.rand(nrows + 1, ncols))
535-
repr_big = repr(bigx)
603+
bigx = DataFrame(np.random.rand(nrows + 1, ncols))
604+
repr_big = repr(bigx)
536605

537-
diff = len(repr_small.splitlines()) - len(repr_big.splitlines())
606+
diff = len(repr_small.splitlines()) - len(repr_big.splitlines())
538607

539-
# the difference in line count is the number of columns
540-
self.assertEqual(diff, expected_difference)
608+
# the difference in line count is the number of columns
609+
self.assertEqual(diff, expected_difference)
541610

542611
test_setting(None)
543612
test_setting(3)
@@ -691,7 +760,7 @@ def test_index_with_nan(self):
691760
result = y.to_string()
692761
expected = u' id1 id3 value\nid2 \nNaN 1a3 78d 123\nNaN 9h4 79d 64'
693762
self.assert_(result == expected)
694-
763+
695764
# partial nan in mi
696765
df2 = df.copy()
697766
df2.ix[:,'id2'] = np.nan
@@ -1170,8 +1239,8 @@ def get_ipython():
11701239
self.assert_(repstr is not None)
11711240

11721241
fmt.set_printoptions(max_rows=5, max_columns=2)
1173-
1174-
self.assert_(self.frame._repr_html_() is None)
1242+
repstr = self.frame._repr_html_()
1243+
self.assert_('class' in repstr) # info fallback
11751244

11761245
fmt.reset_printoptions()
11771246

0 commit comments

Comments
 (0)