diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 884107d4bc6af..486a6a2a02be3 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -3568,6 +3568,7 @@ def _wrap(x, alt_format_): elif formatters is None and float_format is not None: formatters_ = partial(_wrap, alt_format_=lambda v: v) format_index_ = [index_format_, column_format_] + format_index_names_ = [index_format_, column_format_] # Deal with hiding indexes and relabelling column names hide_: list[dict] = [] @@ -3616,6 +3617,7 @@ def _wrap(x, alt_format_): relabel_index=relabel_index_, format={"formatter": formatters_, **base_format_}, format_index=format_index_, + format_index_names=format_index_names_, render_kwargs=render_kwargs_, ) @@ -3628,6 +3630,7 @@ def _to_latex_via_styler( relabel_index: dict | list[dict] | None = None, format: dict | list[dict] | None = None, format_index: dict | list[dict] | None = None, + format_index_names: dict | list[dict] | None = None, render_kwargs: dict | None = None, ): """ @@ -3672,7 +3675,13 @@ def _to_latex_via_styler( self = cast("DataFrame", self) styler = Styler(self, uuid="") - for kw_name in ["hide", "relabel_index", "format", "format_index"]: + for kw_name in [ + "hide", + "relabel_index", + "format", + "format_index", + "format_index_names", + ]: kw = vars()[kw_name] if isinstance(kw, dict): getattr(styler, kw_name)(**kw) diff --git a/pandas/tests/io/formats/test_to_latex.py b/pandas/tests/io/formats/test_to_latex.py index 8d46442611719..ebc6ff5be108f 100644 --- a/pandas/tests/io/formats/test_to_latex.py +++ b/pandas/tests/io/formats/test_to_latex.py @@ -824,6 +824,46 @@ def test_to_latex_escape_special_chars(self): ) assert result == expected + def test_to_latex_escape_special_chars_in_index_names(self): + # https://github.com/pandas-dev/pandas/issues/61309 + # https://github.com/pandas-dev/pandas/issues/57362 + index = "&%$#_{}}~^\\" + df = DataFrame({index: [1, 2, 3]}).set_index(index) + result = df.to_latex(escape=True) + expected = _dedent( + r""" + \begin{tabular}{l} + \toprule + \&\%\$\#\_\{\}\}\textasciitilde \textasciicircum \textbackslash \\ + \midrule + 1 \\ + 2 \\ + 3 \\ + \bottomrule + \end{tabular} + """ + ) + assert result == expected + + def test_to_latex_escape_special_chars_in_column_name(self): + df = DataFrame({"A": [1, 2, 3], "B": ["a", "b", "c"]}) + df.columns.name = "_^~" + result = df.to_latex(escape=True) + expected = _dedent( + r""" + \begin{tabular}{lrl} + \toprule + \_\textasciicircum \textasciitilde & A & B \\ + \midrule + 0 & 1 & a \\ + 1 & 2 & b \\ + 2 & 3 & c \\ + \bottomrule + \end{tabular} + """ + ) + assert result == expected + def test_to_latex_specified_header_special_chars_without_escape(self): # GH 7124 df = DataFrame({"a": [1, 2], "b": ["b1", "b2"]})