Skip to content

ENH: make Styler.concat compatible with to_latex output render #46186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Styler

- New method :meth:`.Styler.to_string` for alternative customisable output methods (:issue:`44502`)
- Added the ability to render ``border`` and ``border-{side}`` CSS properties in Excel (:issue:`42276`)
- Added a new method :meth:`.Styler.concat` which allows adding customised footer rows to visualise additional calculations on the data, e.g. totals and counts etc. (:issue:`43875`)
- Added a new method :meth:`.Styler.concat` which allows adding customised footer rows to visualise additional calculations on the data, e.g. totals and counts etc. (:issue:`43875`, :issue:`46186`)

.. _whatsnew_150.enhancements.enhancement2:

Expand Down
6 changes: 3 additions & 3 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,10 @@ def concat(self, other: Styler) -> Styler:
``format_index`` will be preserved.

.. warning::
Only the output methods ``to_html`` and ``to_string`` currently work with
concatenated Stylers.
Only the output methods ``to_html``, ``to_string`` and ``to_latex``
currently work with concatenated Stylers.

The output methods ``to_latex`` and ``to_excel`` **do not** work with
Other output methods, including ``to_excel``, **do not** work with
concatenated Stylers.

The following should be noted:
Expand Down
42 changes: 32 additions & 10 deletions pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ def _render(
blank: str = "",
):
"""
Computes and applies styles and then generates the general render dicts
Computes and applies styles and then generates the general render dicts.

Also extends the `ctx` and `ctx_index` attributes with those of concatenated
stylers for use within `_translate_latex`
"""
self._compute()
dx = None
Expand All @@ -172,11 +175,17 @@ def _render(
"row": f"{self.css['foot']}_{self.css['row']}",
"foot": self.css["foot"],
}
dx, _ = self.concatenated._render(
dx = self.concatenated._render(
sparse_index, sparse_columns, max_rows, max_cols, blank
)

for (r, c), v in self.concatenated.ctx.items():
self.ctx[(r + len(self.index), c)] = v
for (r, c), v in self.concatenated.ctx_index.items():
self.ctx_index[(r + len(self.index), c)] = v

d = self._translate(sparse_index, sparse_columns, max_rows, max_cols, blank, dx)
return d, dx
return d

def _render_html(
self,
Expand All @@ -190,7 +199,7 @@ def _render_html(
Renders the ``Styler`` including all applied styles to HTML.
Generates a dict with necessary kwargs passed to jinja2 template.
"""
d, _ = self._render(sparse_index, sparse_columns, max_rows, max_cols, " ")
d = self._render(sparse_index, sparse_columns, max_rows, max_cols, " ")
d.update(kwargs)
return self.template_html.render(
**d,
Expand All @@ -204,7 +213,7 @@ def _render_latex(
"""
Render a Styler in latex format
"""
d, _ = self._render(sparse_index, sparse_columns, None, None)
d = self._render(sparse_index, sparse_columns, None, None)
self._translate_latex(d, clines=clines)
self.template_latex.globals["parse_wrap"] = _parse_latex_table_wrapping
self.template_latex.globals["parse_table"] = _parse_latex_table_styles
Expand All @@ -224,7 +233,7 @@ def _render_string(
"""
Render a Styler in string format
"""
d, _ = self._render(sparse_index, sparse_columns, max_rows, max_cols)
d = self._render(sparse_index, sparse_columns, max_rows, max_cols)
d.update(kwargs)
return self.template_string.render(**d)

Expand Down Expand Up @@ -840,11 +849,24 @@ def _translate_latex(self, d: dict, clines: str | None) -> None:
]
for r, row in enumerate(d["head"])
]

def concatenated_visible_rows(obj, n, row_indices):
"""
Extract all visible row indices recursively from concatenated stylers.
"""
row_indices.extend(
[r + n for r in range(len(obj.index)) if r not in obj.hidden_rows]
)
return (
row_indices
if obj.concatenated is None
else concatenated_visible_rows(
obj.concatenated, n + len(obj.index), row_indices
)
)

body = []
for r, row in zip(
[r for r in range(len(self.data.index)) if r not in self.hidden_rows],
d["body"],
):
for r, row in zip(concatenated_visible_rows(self, 0, []), d["body"]):
# note: cannot enumerate d["body"] because rows were dropped if hidden
# during _translate_body so must zip to acquire the true r-index associated
# with the ctx obj which contains the cell styles.
Expand Down
21 changes: 20 additions & 1 deletion pandas/tests/io/formats/style/test_to_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,6 @@ def test_col_format_len(styler):
assert expected in result


@pytest.mark.xfail # concat not yet implemented for to_latex
def test_concat(styler):
result = styler.concat(styler.data.agg(["sum"]).style).to_latex()
expected = dedent(
Expand All @@ -1013,3 +1012,23 @@ def test_concat(styler):
"""
)
assert result == expected


def test_concat_recursion():
# tests hidden row recursion and applied styles
styler1 = DataFrame([[1], [9]]).style.hide([1]).highlight_min(color="red")
styler2 = DataFrame([[9], [2]]).style.hide([0]).highlight_min(color="green")
styler3 = DataFrame([[3], [9]]).style.hide([1]).highlight_min(color="blue")

result = styler1.concat(styler2.concat(styler3)).to_latex(convert_css=True)
expected = dedent(
"""\
\\begin{tabular}{lr}
& 0 \\\\
0 & {\\cellcolor{red}} 1 \\\\
1 & {\\cellcolor{green}} 2 \\\\
0 & {\\cellcolor{blue}} 3 \\\\
\\end{tabular}
"""
)
assert result == expected