Skip to content

Commit 7bc5364

Browse files
committed
changed according to comments
1 parent ce46223 commit 7bc5364

File tree

4 files changed

+102
-119
lines changed

4 files changed

+102
-119
lines changed

pandas/core/indexes/multi.py

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -959,8 +959,7 @@ def _format_attrs(self):
959959
"""
960960
Return a list of tuples of the (attr,formatted_value)
961961
"""
962-
attrs = []
963-
attrs.append(('dtype', "'{}'".format(self.dtype)))
962+
attrs = [('dtype', "'{}'".format(self.dtype))]
964963
if self.names is not None and any(self.names):
965964
attrs.append(('names', default_pprint(self.names)))
966965
max_seq_items = get_option('display.max_seq_items') or len(self)
@@ -976,29 +975,7 @@ def _format_data(self, name=None):
976975
Return the formatted data as a unicode string
977976
"""
978977
return format_object_summary(self, self._formatter_func,
979-
name=name, is_multi=True)
980-
981-
def __unicode__(self):
982-
"""
983-
Return a string representation for this MultiIndex.
984-
985-
Invoked by unicode(df) in py2 only. Yields a Unicode String in both
986-
py2/py3.
987-
"""
988-
klass = self.__class__.__name__
989-
data = self._format_data()
990-
attrs = self._format_attrs()
991-
space = self._format_space()
992-
993-
prepr = (",%s" % space).join("%s=%s" % (k, v) for k, v in attrs)
994-
995-
# no data provided, just attributes
996-
if data is None:
997-
data = ''
998-
999-
res = "%s(%s%s)" % (klass, data, prepr)
1000-
1001-
return res
978+
name=name, line_break_each_value=True)
1002979

1003980
def _format_native_types(self, na_rep='nan', **kwargs):
1004981
new_levels = []

pandas/io/formats/printing.py

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ class TableSchemaFormatter(BaseFormatter):
265265

266266

267267
def format_object_summary(obj, formatter, is_justify=True, name=None,
268-
indent_for_name=True, is_multi=False):
268+
indent_for_name=True, line_break_each_value=False):
269269
"""
270270
Return the formatted obj as a unicode string
271271
@@ -282,8 +282,10 @@ def format_object_summary(obj, formatter, is_justify=True, name=None,
282282
indent_for_name : bool, default True
283283
Whether subsequent lines should be be indented to
284284
align with the name.
285-
is_multi : bool, default False
286-
Is ``obj`` a :class:`MultiIndex` or not
285+
line_break_each_value : bool, default False
286+
If True, inserts a line break for each value of ``obj``.
287+
If False, only break lines when the a line of values gets wider
288+
than the display width
287289
288290
Returns
289291
-------
@@ -308,7 +310,11 @@ def format_object_summary(obj, formatter, is_justify=True, name=None,
308310
space2 = "\n " # space for the opening '['
309311

310312
n = len(obj)
311-
sep = ',' if not is_multi else (',\n ' + ' ' * len(name))
313+
if not line_break_each_value:
314+
sep = ','
315+
else:
316+
# If we want to align on each value, we need a different separator.
317+
sep = (',\n ' + ' ' * len(name))
312318
max_seq_items = get_option('display.max_seq_items') or n
313319

314320
# are we a truncated display
@@ -336,10 +342,10 @@ def best_len(values):
336342

337343
if n == 0:
338344
summary = '[]{}'.format(close)
339-
elif n == 1 and not is_multi:
345+
elif n == 1 and not line_break_each_value:
340346
first = formatter(obj[0])
341347
summary = '[{}]{}'.format(first, close)
342-
elif n == 2 and not is_multi:
348+
elif n == 2 and not line_break_each_value:
343349
first = formatter(obj[0])
344350
last = formatter(obj[-1])
345351
summary = '[{}, {}]{}'.format(first, last, close)
@@ -355,22 +361,31 @@ def best_len(values):
355361

356362
# adjust all values to max length if needed
357363
if is_justify:
358-
head, tail = _justify(head, tail, display_width, best_len,
359-
is_truncated, is_multi)
360-
if is_multi:
364+
if line_break_each_value:
365+
head, tail = _justify(head, tail)
366+
elif (is_truncated or not (len(', '.join(head)) < display_width and
367+
len(', '.join(tail)) < display_width)):
368+
max_length = max(best_len(head), best_len(tail))
369+
head = [x.rjust(max_length) for x in head]
370+
tail = [x.rjust(max_length) for x in tail]
371+
# If we are not truncated and we are only a single
372+
# line, then don't justify
373+
374+
if line_break_each_value:
375+
# truncate vertically if wider than max_space
361376
max_space = display_width - len(space2)
362377
item = tail[0]
363-
for i in reversed(range(1, len(item) + 1)):
364-
if len(_pprint_seq(item, max_seq_items=i)) < max_space:
378+
for max_items in reversed(range(1, len(item) + 1)):
379+
if len(_pprint_seq(item, max_seq_items=max_items)) < max_space:
365380
break
366-
head = [_pprint_seq(x, max_seq_items=i) for x in head]
367-
tail = [_pprint_seq(x, max_seq_items=i) for x in tail]
381+
head = [_pprint_seq(x, max_seq_items=max_items) for x in head]
382+
tail = [_pprint_seq(x, max_seq_items=max_items) for x in tail]
368383

369384
summary = ""
370385
line = space2
371386

372-
for i in range(len(head)):
373-
word = head[i] + sep + ' '
387+
for max_items in range(len(head)):
388+
word = head[max_items] + sep + ' '
374389
summary, line = _extend_line(summary, line, word,
375390
display_width, space2)
376391

@@ -379,8 +394,8 @@ def best_len(values):
379394
summary += line.rstrip() + space2 + '...'
380395
line = space2
381396

382-
for i in range(len(tail) - 1):
383-
word = tail[i] + sep + ' '
397+
for max_items in range(len(tail) - 1):
398+
word = tail[max_items] + sep + ' '
384399
summary, line = _extend_line(summary, line, word,
385400
display_width, space2)
386401

@@ -394,7 +409,7 @@ def best_len(values):
394409
close = ']' + close.rstrip(' ')
395410
summary += close
396411

397-
if len(summary) > (display_width) or is_multi:
412+
if len(summary) > (display_width) or line_break_each_value:
398413
summary += space1
399414
else: # one row
400415
summary += ' '
@@ -405,50 +420,41 @@ def best_len(values):
405420
return summary
406421

407422

408-
def _justify(head, tail, display_width, best_len,
409-
is_truncated=False, is_multi=False):
410-
"""
411-
Justify each item in head and tail, so they align properly.
423+
def _justify(head, tail):
412424
"""
413-
if is_multi:
414-
max_length = _max_level_item_length(head + tail)
415-
head = [tuple(x.rjust(max_len) for x, max_len in zip(seq, max_length))
416-
for seq in head]
417-
tail = [tuple(x.rjust(max_len) for x, max_len in zip(seq, max_length))
418-
for seq in tail]
419-
elif (is_truncated or not (len(', '.join(head)) < display_width and
420-
len(', '.join(tail)) < display_width)):
421-
max_length = max(best_len(head), best_len(tail))
422-
head = [x.rjust(max_length) for x in head]
423-
tail = [x.rjust(max_length) for x in tail]
424-
425-
return head, tail
426-
427-
428-
def _max_level_item_length(seq):
429-
"""
430-
For each position for the sequences in ``seq``, find the largest length.
431-
432-
Used for justifying individual values in a :class:`pandas.MultiIndex`.
425+
Justify each item in each list-like in head and tail, so each item
426+
right-aligns when the two list-likes are stacked vertically.
433427
434428
Parameters
435429
----------
436-
seq : list-like of list-likes of strings
430+
head : list-like of list-likes of strings
431+
tail : list-like of list-likes of strings
437432
438433
Returns
439434
-------
440-
max_length : list of ints
435+
head : list of tuples of strings
436+
tail : list of tuples of strings
441437
442438
Examples
443439
--------
444-
>>> _max_level_item_length([['s', 'ab'], ['abc', 'a']])
445-
[3, 2]
440+
>>> _justify([['a', 'b']], [['abc', 'abcd']])
441+
([(' a', ' b')], [('abc', 'abcd')])
446442
"""
447-
max_length = [0] * len(seq[0])
448-
for inner_seq in seq:
443+
combined = head + tail # type: Sequence[Sequence[str]]
444+
445+
# For each position for the sequences in ``combined``,
446+
# find the length of the largest string.
447+
max_length = [0] * len(combined[0]) # type: List[int]
448+
for inner_seq in combined:
449449
length = [len(item) for item in inner_seq]
450450
max_length = [max(x, y) for x, y in zip(max_length, length)]
451-
return max_length
451+
452+
# justify each item in each list-like in head and tail using max_length
453+
head = [tuple(x.rjust(max_len) for x, max_len in zip(seq, max_length))
454+
for seq in head]
455+
tail = [tuple(x.rjust(max_len) for x, max_len in zip(seq, max_length))
456+
for seq in tail]
457+
return head, tail
452458

453459

454460
def format_object_attrs(obj):

pandas/tests/indexes/multi/conftest.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import numpy as np
22
import pytest
33

4+
import pandas as pd
45
from pandas import Index, MultiIndex
56

67

@@ -52,3 +53,28 @@ def holder():
5253
def compat_props():
5354
# a MultiIndex must have these properties associated with it
5455
return ['shape', 'ndim', 'size']
56+
57+
58+
@pytest.fixture
59+
def narrow_multi_index():
60+
"""
61+
Return a MultiIndex that is narrower than the display (<80 characters).
62+
"""
63+
n = 1000
64+
ci = pd.CategoricalIndex(list('a' * n) + (['abc'] * n))
65+
dti = pd.date_range('2000-01-01', freq='s', periods=n * 2)
66+
return pd.MultiIndex.from_arrays([ci, ci.codes + 9, dti],
67+
names=['a', 'b', 'dti'])
68+
69+
70+
@pytest.fixture
71+
def wide_multi_index():
72+
"""
73+
Return a MultiIndex that is wider than the display (>80 characters).
74+
"""
75+
n = 1000
76+
ci = pd.CategoricalIndex(list('a' * n) + (['abc'] * n))
77+
dti = pd.date_range('2000-01-01', freq='s', periods=n * 2)
78+
levels = [ci, ci.codes + 9, dti, dti, dti]
79+
names = ['a', 'b', 'dti_1', 'dti_2', 'dti_3']
80+
return pd.MultiIndex.from_arrays(levels, names=names)

pandas/tests/indexes/multi/test_format.py

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -55,31 +55,11 @@ def test_repr_with_unicode_data():
5555
assert "\\" not in repr(index) # we don't want unicode-escaped
5656

5757

58-
@pytest.mark.skip(reason="#22511 will remove this test")
59-
def test_repr_roundtrip():
60-
58+
def test_repr_roundtrip_raises():
6159
mi = MultiIndex.from_product([list('ab'), range(3)],
6260
names=['first', 'second'])
63-
str(mi)
64-
65-
tm.assert_index_equal(eval(repr(mi)), mi, exact=True)
66-
67-
mi_u = MultiIndex.from_product(
68-
[list('ab'), range(3)], names=['first', 'second'])
69-
result = eval(repr(mi_u))
70-
tm.assert_index_equal(result, mi_u, exact=True)
71-
72-
# formatting
73-
str(mi)
74-
75-
# long format
76-
mi = MultiIndex.from_product([list('abcdefg'), range(10)],
77-
names=['first', 'second'])
78-
79-
tm.assert_index_equal(eval(repr(mi)), mi, exact=True)
80-
81-
result = eval(repr(mi_u))
82-
tm.assert_index_equal(result, mi_u, exact=True)
61+
with pytest.raises(TypeError):
62+
eval(repr(mi))
8363

8464

8565
def test_unicode_string_with_unicode():
@@ -98,25 +78,16 @@ def test_repr_max_seq_item_setting(idx):
9878

9979
class TestRepr(object):
10080

101-
def setup_class(self):
102-
n = 1000
103-
ci = pd.CategoricalIndex(list('a' * n) + (['abc'] * n))
104-
dti = pd.date_range('2000-01-01', freq='s', periods=n * 2)
105-
self.narrow_mi = pd.MultiIndex.from_arrays([ci, ci.codes + 9, dti],
106-
names=['a', 'b', 'dti'])
107-
108-
levels = [ci, ci.codes + 9, dti, dti, dti]
109-
names = ['a', 'b', 'dti_1', 'dti_2', 'dti_3']
110-
self.wide_mi = pd.MultiIndex.from_arrays(levels, names=names)
111-
11281
def test_repr(self, idx):
11382
result = idx[:1].__repr__()
114-
expected = """MultiIndex([('foo', 'one')],
83+
expected = """\
84+
MultiIndex([('foo', 'one')],
11585
dtype='object', names=['first', 'second'])"""
11686
assert result == expected
11787

11888
result = idx.__repr__()
119-
expected = """MultiIndex([('foo', 'one'),
89+
expected = """\
90+
MultiIndex([('foo', 'one'),
12091
('foo', 'two'),
12192
('bar', 'one'),
12293
('baz', 'two'),
@@ -127,22 +98,24 @@ def test_repr(self, idx):
12798

12899
with pd.option_context('display.max_seq_items', 5):
129100
result = idx.__repr__()
130-
expected = """MultiIndex([('foo', 'one'),
101+
expected = """\
102+
MultiIndex([('foo', 'one'),
131103
('foo', 'two'),
132104
...
133105
('qux', 'one'),
134106
('qux', 'two')],
135107
dtype='object', names=['first', 'second'], length=6)"""
136108
assert result == expected
137109

138-
def test_rjust(self):
139-
result = self.narrow_mi[:1].__repr__()
110+
def test_rjust(self, narrow_multi_index):
111+
mi = narrow_multi_index
112+
result = mi[:1].__repr__()
140113
expected = """\
141114
MultiIndex([('a', 9, '2000-01-01 00:00:00')],
142115
dtype='object', names=['a', 'b', 'dti'])"""
143116
assert result == expected
144117

145-
result = self.narrow_mi[::500].__repr__()
118+
result = mi[::500].__repr__()
146119
expected = """\
147120
MultiIndex([( 'a', 9, '2000-01-01 00:00:00'),
148121
( 'a', 9, '2000-01-01 00:08:20'),
@@ -151,7 +124,7 @@ def test_rjust(self):
151124
dtype='object', names=['a', 'b', 'dti'])"""
152125
assert result == expected
153126

154-
result = self.narrow_mi.__repr__()
127+
result = mi.__repr__()
155128
expected = """\
156129
MultiIndex([( 'a', 9, '2000-01-01 00:00:00'),
157130
( 'a', 9, '2000-01-01 00:00:01'),
@@ -177,13 +150,14 @@ def test_rjust(self):
177150
dtype='object', names=['a', 'b', 'dti'], length=2000)"""
178151
assert result == expected
179152

180-
def test_tuple_width(self):
181-
result = self.wide_mi[:1].__repr__()
153+
def test_tuple_width(self, wide_multi_index):
154+
mi = wide_multi_index
155+
result = mi[:1].__repr__()
182156
expected = """MultiIndex([('a', 9, '2000-01-01 00:00:00', '2000-01-01 00:00:00', ...)],
183157
dtype='object', names=['a', 'b', 'dti_1', 'dti_2', 'dti_3'])"""
184158
assert result == expected
185159

186-
result = self.wide_mi[:10].__repr__()
160+
result = mi[:10].__repr__()
187161
expected = """\
188162
MultiIndex([('a', 9, '2000-01-01 00:00:00', '2000-01-01 00:00:00', ...),
189163
('a', 9, '2000-01-01 00:00:01', '2000-01-01 00:00:01', ...),
@@ -198,7 +172,7 @@ def test_tuple_width(self):
198172
dtype='object', names=['a', 'b', 'dti_1', 'dti_2', 'dti_3'])"""
199173
assert result == expected
200174

201-
result = self.wide_mi.__repr__()
175+
result = mi.__repr__()
202176
expected = """\
203177
MultiIndex([( 'a', 9, '2000-01-01 00:00:00', '2000-01-01 00:00:00', ...),
204178
( 'a', 9, '2000-01-01 00:00:01', '2000-01-01 00:00:01', ...),

0 commit comments

Comments
 (0)