Skip to content

Commit 2524ff4

Browse files
gfyoungTomAugspurger
authored andcommitted
BUG: LatexFormatter.write_result multi-index (pandas-dev#18685)
* BUG: LatexFormatter.write_result multi-index Fixed GH issue 14484: `LatexFormatter.write_result`` now does not print blanks if a higher-order index differs from the previous row. Also added testcase for this. * MAINT: Address reviewer comments Closes pandas-devgh-14484 Closes pandas-devgh-17499
1 parent 11a4100 commit 2524ff4

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed

doc/source/whatsnew/v0.21.1.txt

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ I/O
117117
- Bug in :meth:`DataFrame.to_msgpack` when serializing data of the numpy.bool_ datatype (:issue:`18390`)
118118
- Bug in :func:`read_json` not decoding when reading line deliminted JSON from S3 (:issue:`17200`)
119119
- Bug in :func:`pandas.io.json.json_normalize` to avoid modification of ``meta`` (:issue:`18610`)
120+
- Bug in :func:`to_latex` where repeated multi-index values were not printed even though a higher level index differed from the previous row (:issue:`14484`)
120121

121122

122123
Plotting

pandas/io/formats/format.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import pandas as pd
4646
import numpy as np
4747

48-
import itertools
4948
import csv
5049
from functools import partial
5150

@@ -891,6 +890,7 @@ def get_col_type(dtype):
891890
name = any(self.frame.index.names)
892891
cname = any(self.frame.columns.names)
893892
lastcol = self.frame.index.nlevels - 1
893+
previous_lev3 = None
894894
for i, lev in enumerate(self.frame.index.levels):
895895
lev2 = lev.format()
896896
blank = ' ' * len(lev2[0])
@@ -901,11 +901,19 @@ def get_col_type(dtype):
901901
lev3 = [blank] * clevels
902902
if name:
903903
lev3.append(lev.name)
904-
for level_idx, group in itertools.groupby(
905-
self.frame.index.labels[i]):
906-
count = len(list(group))
907-
lev3.extend([lev2[level_idx]] + [blank] * (count - 1))
904+
current_idx_val = None
905+
for level_idx in self.frame.index.labels[i]:
906+
if ((previous_lev3 is None or
907+
previous_lev3[len(lev3)].isspace()) and
908+
lev2[level_idx] == current_idx_val):
909+
# same index as above row and left index was the same
910+
lev3.append(blank)
911+
else:
912+
# different value than above or left index different
913+
lev3.append(lev2[level_idx])
914+
current_idx_val = lev2[level_idx]
908915
strcols.insert(i, lev3)
916+
previous_lev3 = lev3
909917

910918
column_format = self.column_format
911919
if column_format is None:

pandas/tests/io/formats/test_to_latex.py

+22
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,28 @@ def test_to_latex_multiindex(self):
221221

222222
assert result == expected
223223

224+
def test_to_latex_multiindex_dupe_level(self):
225+
# see gh-14484
226+
#
227+
# If an index is repeated in subsequent rows, it should be
228+
# replaced with a blank in the created table. This should
229+
# ONLY happen if all higher order indices (to the left) are
230+
# equal too. In this test, 'c' has to be printed both times
231+
# because the higher order index 'A' != 'B'.
232+
df = pd.DataFrame(index=pd.MultiIndex.from_tuples(
233+
[('A', 'c'), ('B', 'c')]), columns=['col'])
234+
result = df.to_latex()
235+
expected = r"""\begin{tabular}{lll}
236+
\toprule
237+
& & col \\
238+
\midrule
239+
A & c & NaN \\
240+
B & c & NaN \\
241+
\bottomrule
242+
\end{tabular}
243+
"""
244+
assert result == expected
245+
224246
def test_to_latex_multicolumnrow(self):
225247
df = pd.DataFrame({
226248
('c1', 0): dict((x, x) for x in range(5)),

0 commit comments

Comments
 (0)