Skip to content

Commit e165ddd

Browse files
committed
Merge pull request #7232 from bjonen/fix_7174
BUG: multi-index output formatting is buggy (GH7174)
2 parents cf404da + d170f87 commit e165ddd

File tree

2 files changed

+76
-22
lines changed

2 files changed

+76
-22
lines changed

pandas/core/format.py

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ def write_result(self, buf):
780780

781781
def _write_header(self, indent):
782782
truncate_h = self.fmt.truncate_h
783+
row_levels = self.frame.index.nlevels
783784
if not self.fmt.header:
784785
# write nothing
785786
return indent
@@ -819,13 +820,49 @@ def _column_header():
819820
sentinel = None
820821
levels = self.columns.format(sparsify=sentinel,
821822
adjoin=False, names=False)
822-
823823
level_lengths = _get_level_lengths(levels, sentinel)
824-
825-
row_levels = self.frame.index.nlevels
826-
824+
inner_lvl = len(level_lengths) - 1
827825
for lnum, (records, values) in enumerate(zip(level_lengths,
828826
levels)):
827+
if truncate_h:
828+
# modify the header lines
829+
ins_col = self.fmt.tr_col_num
830+
if self.fmt.sparsify:
831+
recs_new = {}
832+
# Increment tags after ... col.
833+
for tag,span in list(records.items()):
834+
if tag >= ins_col:
835+
recs_new[tag + 1] = span
836+
elif tag + span > ins_col:
837+
recs_new[tag] = span + 1
838+
if lnum == inner_lvl:
839+
values = values[:ins_col] + (u('...'),) + \
840+
values[ins_col:]
841+
else: # sparse col headers do not receive a ...
842+
values = values[:ins_col] + \
843+
(values[ins_col - 1],) + values[ins_col:]
844+
else:
845+
recs_new[tag] = span
846+
# if ins_col lies between tags, all col headers get ...
847+
if tag + span == ins_col:
848+
recs_new[ins_col] = 1
849+
values = values[:ins_col] + (u('...'),) + \
850+
values[ins_col:]
851+
records = recs_new
852+
inner_lvl = len(level_lengths) - 1
853+
if lnum == inner_lvl:
854+
records[ins_col] = 1
855+
else:
856+
recs_new = {}
857+
for tag,span in list(records.items()):
858+
if tag >= ins_col:
859+
recs_new[tag + 1] = span
860+
else:
861+
recs_new[tag] = span
862+
recs_new[ins_col] = 1
863+
records = recs_new
864+
values = values[:ins_col] + [u('...')] + values[ins_col:]
865+
829866
name = self.columns.names[lnum]
830867
row = [''] * (row_levels - 1) + ['' if name is None
831868
else com.pprint_thing(name)]
@@ -839,25 +876,15 @@ def _column_header():
839876
continue
840877
j += 1
841878
row.append(v)
842-
if truncate_h:
843-
if self.fmt.sparsify and lnum == 0:
844-
ins_col = row_levels + self.fmt.tr_col_num - 1
845-
row.insert(ins_col, '...')
846-
847-
for tag in list(tags.keys()):
848-
if tag >= ins_col:
849-
tags[tag+1] = tags.pop(tag)
850-
else:
851-
row.insert(row_levels + self.fmt.tr_col_num, '...')
852-
853879
self.write_tr(row, indent, self.indent_delta, tags=tags,
854880
header=True)
855881
else:
856882
col_row = _column_header()
857883
align = self.fmt.justify
858884

859885
if truncate_h:
860-
col_row.insert(self.fmt.tr_col_num + 1, '...')
886+
ins_col = row_levels + self.fmt.tr_col_num
887+
col_row.insert(ins_col, '...')
861888

862889
self.write_tr(col_row, indent, self.indent_delta, header=True,
863890
align=align)
@@ -866,6 +893,9 @@ def _column_header():
866893
row = [
867894
x if x is not None else '' for x in self.frame.index.names
868895
] + [''] * min(len(self.columns), self.max_cols)
896+
if truncate_h:
897+
ins_col = row_levels + self.fmt.tr_col_num
898+
row.insert(ins_col, '')
869899
self.write_tr(row, indent, self.indent_delta, header=True)
870900

871901
indent -= self.indent_delta
@@ -948,12 +978,36 @@ def _write_hierarchical_rows(self, fmt_values, indent):
948978
adjoin=False, names=False)
949979

950980
level_lengths = _get_level_lengths(levels, sentinel)
981+
inner_lvl = len(level_lengths) - 1
982+
if truncate_v:
983+
# Insert ... row and adjust idx_values and
984+
# level_lengths to take this into account.
985+
ins_row = self.fmt.tr_row_num
986+
for lnum,records in enumerate(level_lengths):
987+
rec_new = {}
988+
for tag,span in list(records.items()):
989+
if tag >= ins_row:
990+
rec_new[tag + 1] = span
991+
elif tag + span > ins_row:
992+
rec_new[tag] = span + 1
993+
dot_row = list(idx_values[ins_row - 1])
994+
dot_row[-1] = u('...')
995+
idx_values.insert(ins_row,tuple(dot_row))
996+
else:
997+
rec_new[tag] = span
998+
# If ins_row lies between tags, all cols idx cols receive ...
999+
if tag + span == ins_row:
1000+
rec_new[ins_row] = 1
1001+
if lnum == 0:
1002+
idx_values.insert(ins_row,tuple([u('...')]*len(level_lengths)))
1003+
level_lengths[lnum] = rec_new
1004+
1005+
level_lengths[inner_lvl][ins_row] = 1
1006+
for ix_col in range(len(fmt_values)):
1007+
fmt_values[ix_col].insert(ins_row,'...')
1008+
nrows += 1
9511009

9521010
for i in range(nrows):
953-
if truncate_v and i == (self.fmt.tr_row_num):
954-
str_sep_row = [ '...' ] * (len(row) + sparse_offset)
955-
self.write_tr(str_sep_row, indent, self.indent_delta, tags=None)
956-
9571011
row = []
9581012
tags = {}
9591013

pandas/tests/test_format.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -953,8 +953,8 @@ def test_to_html_truncate_multi_index(self):
953953
<td> NaN</td>
954954
</tr>
955955
<tr>
956-
<td>...</td>
957-
<td>...</td>
956+
<th>...</th>
957+
<th>...</th>
958958
<td>...</td>
959959
<td>...</td>
960960
<td>...</td>

0 commit comments

Comments
 (0)