Skip to content
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Styler
- :meth:`.Styler.to_html` introduces keyword arguments ``sparse_index``, ``sparse_columns``, ``bold_headers``, ``caption`` (:issue:`41946`, :issue:`43149`).
- Keyword argument ``level`` is added to :meth:`.Styler.hide_index` and :meth:`.Styler.hide_columns` for optionally controlling hidden levels in a MultiIndex (:issue:`25475`)
- Global options have been extended to configure default ``Styler`` properties including formatting and encoding and mathjax options and LaTeX (:issue:`41395`)
- Naive sparsification is now possible for LaTeX without the multirow package (:issue:`43369`)

Formerly Styler relied on ``display.html.use_mathjax``, which has now been replaced by ``styler.html.mathjax``.

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/config_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ def register_converter_cb(key):
"latex.multirow_align",
"c",
styler_multirow_align,
validator=is_one_of_factory(["c", "t", "b"]),
validator=is_one_of_factory(["c", "t", "b", "naive"]),
)

cf.register_option(
Expand Down
9 changes: 6 additions & 3 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,13 @@ def to_latex(
Whether to sparsify the display of a hierarchical index. Setting to False
will display each explicit level element in a hierarchical key for each
column. Defaults to ``pandas.options.styler.sparse.columns`` value.
multirow_align : {"c", "t", "b"}, optional
multirow_align : {"c", "t", "b", "naive"}, optional
If sparsifying hierarchical MultiIndexes whether to align text centrally,
at the top or bottom. If not given defaults to
``pandas.options.styler.latex.multirow_align``
at the top or bottom using the multirow package. If not given defaults to
``pandas.options.styler.latex.multirow_align``. If "naive" is given renders
without multirow.

.. versionchanged:: 1.4.0
multicol_align : {"r", "c", "l"}, optional
If sparsifying hierarchical MultiIndex columns whether to align text at
the left, centrally, or at the right. If not given defaults to
Expand Down
2 changes: 2 additions & 0 deletions pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,8 @@ def _parse_latex_header_span(
colspan = int(colspan[: colspan.find('"')])
return f"\\multicolumn{{{colspan}}}{{{multicol_align}}}{{{display_val}}}"
elif 'rowspan="' in attrs:
if multirow_align == "naive":
return display_val
rowspan = attrs[attrs.find('rowspan="') + 9 :]
rowspan = int(rowspan[: rowspan.find('"')])
return f"\\multirow[{multirow_align}]{{{rowspan}}}{{*}}{{{display_val}}}"
Expand Down
19 changes: 19 additions & 0 deletions pandas/tests/io/formats/style/test_to_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,25 @@ def test_multiindex_row(df):
assert expected == s.to_latex(sparse_index=False)


def test_multirow_naive(df):
ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that the letters A and B should not be repeated in both the index and columns.
What if you use these for the index?

ridx = MultiIndex.from_tuples([("X", "x"), ("X", "y"), ("Z", "z")])

I guess it will be easier to differentiate from the columns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fully agree. silly oversight.

df.loc[2, :] = [2, -2.22, "de"]
df = df.astype({"A": int})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider simplifying the dataframe creation to this:

df = DataFrame( {"A": [0, 1, 2], "B": [-0.61, -1.22, -2.22], "C": ["ab", "cd", "de"]}, index=ridx, )

This way, it is clearer what the dataframe actually look like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used in multiple tests so I cleaned up module with a fixture

df.index = ridx
expected = dedent(
"""\
\\begin{tabular}{llrrl}
& & A & B & C \\\\
A & a & 0 & -0.61 & ab \\\\
& b & 1 & -1.22 & cd \\\\
B & c & 2 & -2.22 & de \\\\
\\end{tabular}
"""
)
s = df.style.format(precision=2)
assert expected == s.to_latex(multirow_align="naive")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that result variable is extracted:

result = s.to_latex(multirow_align="naive") assert result == expected

Besides, s is not a preferred variable name, because one-letter variables make debugging more complicated.
What about making it more explicit, styler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes cleaned up module wide (somewhat)



def test_multiindex_row_and_col(df):
cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")])
ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")])
Expand Down