diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index 4e2c836a9db8d..09da9f04f8360 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -896,7 +896,7 @@ def register_converter_cb(key): "latex.multicol_align", "r", styler_multicol_align, - validator=is_one_of_factory(["r", "c", "l"]), + validator=is_one_of_factory(["r", "c", "l", "naive-l", "naive-r"]), ) cf.register_option( diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 92bd4bcc7ced1..2a063501976da 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -527,10 +527,13 @@ def to_latex( without multirow. .. versionchanged:: 1.4.0 - multicol_align : {"r", "c", "l"}, optional + multicol_align : {"r", "c", "l", "naive-l", "naive-r"}, optional If sparsifying hierarchical MultiIndex columns whether to align text at the left, centrally, or at the right. If not given defaults to - ``pandas.options.styler.latex.multicol_align`` + ``pandas.options.styler.latex.multicol_align``. If a naive option is + given renders without multicol. + + .. versionchanged:: 1.4.0 siunitx : bool, default False Set to ``True`` to structure LaTeX compatible with the {siunitx} package. environment : str, optional diff --git a/pandas/io/formats/style_render.py b/pandas/io/formats/style_render.py index 6be75262a64ca..8c5af730a5fc7 100644 --- a/pandas/io/formats/style_render.py +++ b/pandas/io/formats/style_render.py @@ -1410,6 +1410,14 @@ def _parse_latex_header_span( if 'colspan="' in attrs: colspan = attrs[attrs.find('colspan="') + 9 :] # len('colspan="') = 9 colspan = int(colspan[: colspan.find('"')]) + if "naive-l" == multicol_align: + out = f"{{{display_val}}}" if wrap else f"{display_val}" + blanks = " & {}" if wrap else " &" + return out + blanks * (colspan - 1) + elif "naive-r" == multicol_align: + out = f"{{{display_val}}}" if wrap else f"{display_val}" + blanks = "{} & " if wrap else "& " + return blanks * (colspan - 1) + out return f"\\multicolumn{{{colspan}}}{{{multicol_align}}}{{{display_val}}}" elif 'rowspan="' in attrs: if multirow_align == "naive": diff --git a/pandas/tests/io/formats/style/test_to_latex.py b/pandas/tests/io/formats/style/test_to_latex.py index 3df94c4eb0a02..40ba3ca26afa4 100644 --- a/pandas/tests/io/formats/style/test_to_latex.py +++ b/pandas/tests/io/formats/style/test_to_latex.py @@ -302,6 +302,35 @@ def test_multiindex_row_and_col(df_ext): assert result == expected +@pytest.mark.parametrize( + "multicol_align, siunitx, header", + [ + ("naive-l", False, " & A & &"), + ("naive-r", False, " & & & A"), + ("naive-l", True, "{} & {A} & {} & {}"), + ("naive-r", True, "{} & {} & {} & {A}"), + ], +) +def test_multicol_naive(df, multicol_align, siunitx, header): + ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("A", "c")]) + df.columns = ridx + level1 = " & a & b & c" if not siunitx else "{} & {a} & {b} & {c}" + col_format = "lrrl" if not siunitx else "lSSl" + expected = dedent( + f"""\ + \\begin{{tabular}}{{{col_format}}} + {header} \\\\ + {level1} \\\\ + 0 & 0 & -0.61 & ab \\\\ + 1 & 1 & -1.22 & cd \\\\ + \\end{{tabular}} + """ + ) + styler = df.style.format(precision=2) + result = styler.to_latex(multicol_align=multicol_align, siunitx=siunitx) + assert expected == result + + def test_multi_options(df_ext): cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")]) ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")])