From e81e96ec820dc035598c00b6c0fd04b2238ad99b Mon Sep 17 00:00:00 2001 From: Pietro Battiston Date: Wed, 6 Dec 2017 20:27:49 +0100 Subject: [PATCH] BUG: do not escape empty placeholder in to_latex() closes #18667 --- doc/source/whatsnew/v0.22.0.txt | 2 +- pandas/io/formats/format.py | 2 +- pandas/tests/io/formats/test_to_latex.py | 32 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt index d34c1f3535509..ca7c43d1f2b31 100644 --- a/doc/source/whatsnew/v0.22.0.txt +++ b/doc/source/whatsnew/v0.22.0.txt @@ -302,7 +302,7 @@ I/O - Bug in :func:`read_msgpack` with a non existent file is passed in Python 2 (:issue:`15296`) - Bug in :func:`read_csv` where a ``MultiIndex`` with duplicate columns was not being mangled appropriately (:issue:`18062`) - Bug in :func:`read_sas` where a file with 0 variables gave an ``AttributeError`` incorrectly. Now it gives an ``EmptyDataError`` (:issue:`18184`) -- +- Bug in :func:`DataFrame.to_latex()` where pairs of braces meant to serve as invisible placeholders were escaped (:issue:`18667`) - Plotting diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 8f25eb3af70cd..4b7f4de12bb65 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -967,7 +967,7 @@ def get_col_type(dtype): .replace('#', '\\#').replace('{', '\\{') .replace('}', '\\}').replace('~', '\\textasciitilde') .replace('^', '\\textasciicircum').replace('&', '\\&') - if x else '{}') for x in row] + if (x and x != '{}') else '{}') for x in row] else: crow = [x if x else '{}' for x in row] if self.bold_rows and self.fmt.index: diff --git a/pandas/tests/io/formats/test_to_latex.py b/pandas/tests/io/formats/test_to_latex.py index 35ef5a1cf5c72..549a1e1ce316a 100644 --- a/pandas/tests/io/formats/test_to_latex.py +++ b/pandas/tests/io/formats/test_to_latex.py @@ -536,3 +536,35 @@ def test_to_latex_no_bold_rows(self): \end{tabular} """ assert observed == expected + + @pytest.mark.parametrize('name0', [None, 'named']) + @pytest.mark.parametrize('name1', [None, 'named']) + @pytest.mark.parametrize('axes', [[0], [1], [0, 1]]) + def test_to_latex_multiindex_names(self, name0, name1, axes): + # GH 18667 + names = [name0, name1] + mi = pd.MultiIndex.from_product([[1, 2], [3, 4]]) + df = pd.DataFrame(-1, index=mi.copy(), columns=mi.copy()) + for idx in axes: + df.axes[idx].names = names + + idx_names = tuple(n or '{}' for n in names) + idx_names_row = ('%s & %s & & & & \\\\\n' % idx_names + if (0 in axes and any(names)) else '') + placeholder = '{}' if any(names) and 1 in axes else ' ' + col_names = [n if (bool(n) and 1 in axes) else placeholder + for n in names] + observed = df.to_latex() + expected = r"""\begin{tabular}{llrrrr} +\toprule + & %s & \multicolumn{2}{l}{1} & \multicolumn{2}{l}{2} \\ + & %s & 3 & 4 & 3 & 4 \\ +%s\midrule +1 & 3 & -1 & -1 & -1 & -1 \\ + & 4 & -1 & -1 & -1 & -1 \\ +2 & 3 & -1 & -1 & -1 & -1 \\ + & 4 & -1 & -1 & -1 & -1 \\ +\bottomrule +\end{tabular} +""" % tuple(list(col_names) + [idx_names_row]) + assert observed == expected