-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Style concats #49212
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
Style concats #49212
Changes from 17 commits
b3bc28b
4d77906
aa1484f
6de1944
8d41210
dd4d29d
0d5a258
8cfc69c
28d937e
6c85396
5703836
4532396
fe4be54
e2c42cc
20ce639
4bc20d1
2698deb
eff3d92
c1cee9d
9cfb970
e416060
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
.. _whatsnew_153: | ||
|
||
What's new in 1.5.3 (??) | ||
--------------------------------------- | ||
|
||
These are the changes in pandas 1.5.3. See :ref:`release` for a full changelog | ||
including other versions of pandas. | ||
|
||
{{ header }} | ||
|
||
.. --------------------------------------------------------------------------- | ||
.. _whatsnew_153.regressions: | ||
|
||
Fixed regressions | ||
~~~~~~~~~~~~~~~~~ | ||
- | ||
- | ||
|
||
.. --------------------------------------------------------------------------- | ||
.. _whatsnew_153.bug_fixes: | ||
|
||
Bug fixes | ||
~~~~~~~~~ | ||
- Bug when chaining several :meth:`.Styler.concat` calls, only the last styler was concatenated (:issue:`49207`) | ||
- | ||
|
||
.. --------------------------------------------------------------------------- | ||
.. _whatsnew_153.other: | ||
|
||
Other | ||
~~~~~ | ||
- | ||
- | ||
|
||
.. --------------------------------------------------------------------------- | ||
.. _whatsnew_153.contributors: | ||
|
||
Contributors | ||
~~~~~~~~~~~~ | ||
|
||
.. contributors:: v1.5.2..v1.5.3|HEAD |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,7 +119,7 @@ def __init__( | |
"blank": "blank", | ||
"foot": "foot", | ||
} | ||
self.concatenated: StylerRenderer | None = None | ||
self.concatenated: list[StylerRenderer] = [] | ||
attack68 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# add rendering variables | ||
self.hide_index_names: bool = False | ||
self.hide_column_names: bool = False | ||
|
@@ -161,27 +161,34 @@ def _render( | |
stylers for use within `_translate_latex` | ||
""" | ||
self._compute() | ||
dx = None | ||
if self.concatenated is not None: | ||
self.concatenated.hide_index_ = self.hide_index_ | ||
self.concatenated.hidden_columns = self.hidden_columns | ||
self.concatenated.css = { | ||
dxs = [] | ||
ctx_len = len(self.index) | ||
for i, concatenated in enumerate(self.concatenated): | ||
concatenated.hide_index_ = self.hide_index_ | ||
concatenated.hidden_columns = self.hidden_columns | ||
foot = f"{self.css['foot']}{i}" | ||
concatenated.css = { | ||
**self.css, | ||
"data": f"{self.css['foot']}_{self.css['data']}", | ||
"row_heading": f"{self.css['foot']}_{self.css['row_heading']}", | ||
"row": f"{self.css['foot']}_{self.css['row']}", | ||
"foot": self.css["foot"], | ||
"data": f"{foot}_data", | ||
"row_heading": f"{foot}_row_heading", | ||
"row": f"{foot}_row", | ||
"foot": f"{foot}_foot", | ||
} | ||
dx = self.concatenated._render( | ||
dx = concatenated._render( | ||
sparse_index, sparse_columns, max_rows, max_cols, blank | ||
) | ||
dxs.append(dx) | ||
|
||
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 | ||
for (r, c), v in concatenated.ctx.items(): | ||
tsvikas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.ctx[(r + ctx_len, c)] = v | ||
for (r, c), v in concatenated.ctx_index.items(): | ||
self.ctx_index[(r + ctx_len, c)] = v | ||
|
||
d = self._translate(sparse_index, sparse_columns, max_rows, max_cols, blank, dx) | ||
ctx_len += len(concatenated.index) | ||
|
||
d = self._translate( | ||
sparse_index, sparse_columns, max_rows, max_cols, blank, dxs | ||
) | ||
return d | ||
|
||
def _render_html( | ||
|
@@ -258,7 +265,7 @@ def _translate( | |
max_rows: int | None = None, | ||
max_cols: int | None = None, | ||
blank: str = " ", | ||
dx: dict | None = None, | ||
dxs: list[dict] | None = None, | ||
): | ||
""" | ||
Process Styler data and settings into a dict for template rendering. | ||
|
@@ -278,15 +285,17 @@ def _translate( | |
Specific max rows and cols. max_elements always take precedence in render. | ||
blank : str | ||
Entry to top-left blank cells. | ||
dx : dict | ||
The render dict of the concatenated Styler. | ||
dxs : list[dict] | ||
The render dicts of the concatenated Stylers. | ||
|
||
Returns | ||
------- | ||
d : dict | ||
The following structure: {uuid, table_styles, caption, head, body, | ||
cellstyle, table_attributes} | ||
""" | ||
if dxs is None: | ||
dxs = [] | ||
self.css["blank_value"] = blank | ||
|
||
# construct render dict | ||
|
@@ -340,10 +349,12 @@ def _translate( | |
] | ||
d.update({k: map}) | ||
|
||
if dx is not None: # self.concatenated is not None | ||
for dx in dxs: # self.concatenated is not empty | ||
d["body"].extend(dx["body"]) # type: ignore[union-attr] | ||
d["cellstyle"].extend(dx["cellstyle"]) # type: ignore[union-attr] | ||
d["cellstyle_index"].extend(dx["cellstyle"]) # type: ignore[union-attr] | ||
d["cellstyle_index"].extend( # type: ignore[union-attr] | ||
dx["cellstyle_index"] | ||
) | ||
|
||
table_attr = self.table_attributes | ||
if not get_option("styler.html.mathjax"): | ||
|
@@ -847,23 +858,25 @@ 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): | ||
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 | ||
) | ||
) | ||
n += len(obj.index) | ||
for concatenated in obj.concatenated: | ||
n = _concatenated_visible_rows(concatenated, n, row_indices) | ||
return n | ||
|
||
def concatenated_visible_rows(obj): | ||
row_indices: list[int] = [] | ||
_concatenated_visible_rows(obj, 0, row_indices) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this function (and the one above) be refactored to just recursively yield each row, since it's just needed in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't appear to have been addressed (please don't resolve unresolved conversations) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree this could be considered for refactor to be made simpler. Does it have to be a blocker for the PR - it is otherwise a very nice addition that addresses a potentially common use case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure can be a follow up but it would be good to add a TODO comment here with the note to simplify
mroeschke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return row_indices | ||
|
||
body = [] | ||
for r, row in zip(concatenated_visible_rows(self, 0, []), d["body"]): | ||
for r, row in zip(concatenated_visible_rows(self), 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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if there is only 1 Styler, before
foot_
would be prepended and nowfoot0_
will be prepended?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't appear to have been addressed (please don't resolve unresolved conversations)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When chaining multiple Stylers the CSS needs a unique identifier. Previously it was only possible to compound multiple styler concatenations:
which resulted in CSS classes
None
,foot_
andfoot_foot_
.When allowing chained stylers you need an integer id, so
results in
None
,foot0_
,foot1_
andfoot1_foot0_
,foot2_
.The CSS classes were not documented in 1.5.0 previously and not exposed to the user.
Here they are now amended and documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So just to confirm, a result with
foot_
wasn't visible to the user previously and wouldn't see thatfoot0_
would now be returned?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foot_
is returned as part of the HTML string in 1.5.0. All of the automatically generated styling CSS ids referencefoot_
so that the rendered HTML table, including styles works correctly.Now the HTML string returned will contain
foot0_
and all the auto generated CSS ids will reference that instead.Unless the user is specifically adding a CSS rule for
foot
either with an external stylesheet or usingset_table_styles
there will be no visible difference in the rendered HTML display, either in a JupyterLab or browser.i.e.
will display the same in both versions even though the CSS class names have been changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay thanks for the explanation!