Skip to content

Commit

Permalink
ENH: make Styler.concat compatible with to_latex output render (p…
Browse files Browse the repository at this point in the history
  • Loading branch information
attack68 authored Mar 1, 2022
1 parent a22bdc5 commit 38145a3
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 15 deletions.
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 @@ -300,10 +300,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

0 comments on commit 38145a3

Please sign in to comment.