-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
BUG: styler multiindex hiding indexes and columns alignment #43649
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
Changes from 3 commits
9743099
8a0253e
74c418e
70535c5
2fbe569
7903723
6a2793c
d227914
7ca5002
f27f7ed
a1000a7
c44dcda
c4c9aaa
c22cf0d
0e0b46e
1f3bbec
021bc26
4ba3dff
7fee05d
baa3233
f4ad390
22b03e3
db214d8
771d056
24952ae
1d47d0f
566738d
230138a
b9ba9ea
ea2bba1
75de033
4c34bc2
68ee832
ca70491
7a1994e
91320f8
92c1941
aad0e16
61b24ed
d4c5715
aa0172f
84a814f
f06b727
131070f
a048122
4931f32
e9716f2
d983464
0f11a62
16946b0
451d6d5
6e71e8c
fd76da9
042148c
66fc164
e8b24f0
b1ba432
d54779b
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 |
---|---|---|
|
@@ -497,119 +497,131 @@ def _translate_body( | |
|
||
body = [] | ||
for r, row_tup in enumerate(self.data.itertuples()): | ||
if r >= max_rows: # used only to add a '...' trimmed row: | ||
index_headers = [ | ||
_element( | ||
"th", | ||
f"{row_heading_class} level{c} {trimmed_row_class}", | ||
"...", | ||
not self.hide_index_[c], | ||
attributes="", | ||
) | ||
for c in range(self.data.index.nlevels) | ||
] | ||
|
||
data = [ | ||
_element( | ||
"td", | ||
f"{data_class} col{c} {trimmed_row_class}", | ||
"...", | ||
(c not in self.hidden_columns), | ||
attributes="", | ||
) | ||
for c in range(max_cols) | ||
] | ||
if r not in self.hidden_rows: | ||
if r >= max_rows: # used only to add a '...' trimmed row: | ||
index_headers = [ | ||
_element( | ||
"th", | ||
f"{row_heading_class} level{c} {trimmed_row_class}", | ||
"...", | ||
not self.hide_index_[c], | ||
attributes="", | ||
) | ||
for c in range(self.data.index.nlevels) | ||
] | ||
|
||
if len(self.data.columns) > max_cols: | ||
# columns are also trimmed so we add the final element | ||
data.append( | ||
data = [ | ||
_element( | ||
"td", | ||
f"{data_class} {trimmed_row_class} {trimmed_col_class}", | ||
f"{data_class} col{c} {trimmed_row_class}", | ||
"...", | ||
True, | ||
(c not in self.hidden_columns), | ||
attributes="", | ||
) | ||
) | ||
for c in range(max_cols) | ||
] | ||
|
||
body.append(index_headers + data) | ||
break | ||
if len(self.data.columns) > max_cols: | ||
# columns are also trimmed so we add the final element | ||
data.append( | ||
_element( | ||
"td", | ||
f"{data_class} {trimmed_row_class} {trimmed_col_class}", | ||
"...", | ||
True, | ||
attributes="", | ||
) | ||
) | ||
|
||
index_headers = [] | ||
for c, value in enumerate(rlabels[r]): | ||
header_element = _element( | ||
"th", | ||
f"{row_heading_class} level{c} row{r}", | ||
value, | ||
_is_visible(r, c, idx_lengths) and not self.hide_index_[c], | ||
display_value=self._display_funcs_index[(r, c)](value), | ||
attributes=( | ||
f'rowspan="{idx_lengths.get((c, r), 0)}"' | ||
if idx_lengths.get((c, r), 0) > 1 | ||
else "" | ||
), | ||
) | ||
body.append(index_headers + data) | ||
break | ||
|
||
if self.cell_ids: | ||
header_element["id"] = f"level{c}_row{r}" # id is specified | ||
if (r, c) in self.ctx_index and self.ctx_index[r, c]: | ||
# always add id if a style is specified | ||
header_element["id"] = f"level{c}_row{r}" | ||
self.cellstyle_map_index[tuple(self.ctx_index[r, c])].append( | ||
f"level{c}_row{r}" | ||
index_headers = [] | ||
for c, value in enumerate(rlabels[r]): | ||
header_element = _element( | ||
"th", | ||
f"{row_heading_class} level{c} row{r}", | ||
value, | ||
_is_visible(r, c, idx_lengths) and not self.hide_index_[c], | ||
display_value=self._display_funcs_index[(r, c)](value), | ||
attributes=( | ||
f'rowspan="{idx_lengths.get((c, r), 0)}"' | ||
if idx_lengths.get((c, r), 0) > 1 | ||
else "" | ||
), | ||
) | ||
|
||
index_headers.append(header_element) | ||
if self.cell_ids: | ||
header_element["id"] = f"level{c}_row{r}" # id is specified | ||
if (r, c) in self.ctx_index and self.ctx_index[r, c]: | ||
# always add id if a style is specified | ||
header_element["id"] = f"level{c}_row{r}" | ||
self.cellstyle_map_index[tuple(self.ctx_index[r, c])].append( | ||
f"level{c}_row{r}" | ||
) | ||
|
||
data = [] | ||
for c, value in enumerate(row_tup[1:]): | ||
if c >= max_cols: | ||
data.append( | ||
_element( | ||
"td", | ||
f"{data_class} row{r} {trimmed_col_class}", | ||
"...", | ||
True, | ||
attributes="", | ||
index_headers.append(header_element) | ||
|
||
data = [] | ||
for c, value in enumerate(row_tup[1:]): | ||
if c >= max_cols: | ||
data.append( | ||
_element( | ||
"td", | ||
f"{data_class} row{r} {trimmed_col_class}", | ||
"...", | ||
True, | ||
attributes="", | ||
) | ||
) | ||
) | ||
break | ||
break | ||
|
||
# add custom classes from cell context | ||
cls = "" | ||
if (r, c) in self.cell_context: | ||
cls = " " + self.cell_context[r, c] | ||
|
||
data_element = _element( | ||
"td", | ||
f"{data_class} row{r} col{c}{cls}", | ||
value, | ||
(c not in self.hidden_columns and r not in self.hidden_rows), | ||
attributes="", | ||
display_value=self._display_funcs[(r, c)](value), | ||
) | ||
# add custom classes from cell context | ||
cls = "" | ||
if (r, c) in self.cell_context: | ||
cls = " " + self.cell_context[r, c] | ||
|
||
if self.cell_ids: | ||
data_element["id"] = f"row{r}_col{c}" | ||
if (r, c) in self.ctx and self.ctx[r, c]: | ||
# always add id if needed due to specified style | ||
data_element["id"] = f"row{r}_col{c}" | ||
self.cellstyle_map[tuple(self.ctx[r, c])].append(f"row{r}_col{c}") | ||
data_element = _element( | ||
"td", | ||
f"{data_class} row{r} col{c}{cls}", | ||
value, | ||
(c not in self.hidden_columns and r not in self.hidden_rows), | ||
attributes="", | ||
display_value=self._display_funcs[(r, c)](value), | ||
) | ||
|
||
if self.cell_ids: | ||
data_element["id"] = f"row{r}_col{c}" | ||
if (r, c) in self.ctx and self.ctx[r, c]: | ||
# always add id if needed due to specified style | ||
data_element["id"] = f"row{r}_col{c}" | ||
self.cellstyle_map[tuple(self.ctx[r, c])].append( | ||
f"row{r}_col{c}" | ||
) | ||
|
||
data.append(data_element) | ||
data.append(data_element) | ||
|
||
body.append(index_headers + data) | ||
body.append(index_headers + data) | ||
return body | ||
|
||
def _translate_latex(self, d: dict) -> None: | ||
r""" | ||
Post-process the default render dict for the LaTeX template format. | ||
|
||
Processing items included are: | ||
- Remove hidden columns from the non-headers part of the body. | ||
- Place cellstyles directly in td cells rather than use cellstyle_map. | ||
- Remove hidden indexes or reinsert missing th elements if part of multiindex | ||
or multirow sparsification (so that \multirow and \multicol work correctly). | ||
|
||
1) Remove hidden columns from the non-headers part of the body. This is done | ||
so that there are no repeated "&" latex separators generated from the template. | ||
Alternatively this logic could be refactored into the template/template | ||
parsing function. | ||
|
||
2) Place cellstyles directly in td cells rather than use cellstyle_map. This is | ||
necessary for a LaTeX format where styles have to be coded for each cell | ||
specifically. | ||
|
||
3) Remove hidden indexes or reinsert missing th elements if part of multiindex | ||
or multirow sparsification (so that \multirow and \multicol work correctly), and | ||
there are the correct number of cells (possibly blank) in a row. | ||
""" | ||
d["head"] = [ | ||
[ | ||
|
@@ -619,8 +631,16 @@ def _translate_latex(self, d: dict) -> None: | |
] | ||
for r, row in enumerate(d["head"]) | ||
] | ||
|
||
body = [] | ||
for r, row in enumerate(d["body"]): | ||
index_levels = self.data.index.nlevels | ||
for r, row in zip( | ||
[r for r in range(len(self.data.index)) if r not in self.hidden_rows], | ||
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. might be worth a property to have this 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 is only needed for latex, so for most cases not necessary and might add additional overhead for large HTML (usually latex tables quite a lot smaller) 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. took a second look here. |
||
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. | ||
if all(self.hide_index_): | ||
row_body_headers = [] | ||
else: | ||
|
@@ -632,13 +652,13 @@ def _translate_latex(self, d: dict) -> None: | |
else "", | ||
"cellstyle": self.ctx_index[r, c] if col["is_visible"] else [], | ||
} | ||
for c, col in enumerate(row) | ||
if col["type"] == "th" | ||
for c, col in enumerate(row[:index_levels]) | ||
if (col["type"] == "th" and not self.hide_index_[c]) | ||
] | ||
|
||
row_body_cells = [ | ||
{**col, "cellstyle": self.ctx[r, c - self.data.index.nlevels]} | ||
for c, col in enumerate(row) | ||
{**col, "cellstyle": self.ctx[r, c]} | ||
for c, col in enumerate(row[index_levels:]) | ||
if (col["is_visible"] and col["type"] == "td") | ||
] | ||
|
||
|
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.
only added this line to this method and all code below it was indended one tab.
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.
might be worth splitting up translate_body to some function calls to make it simpler to grok (future)
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.
yeh agreed, i already did this once splitting _translate into header and body and now it has acquired some more stuff, like min/mix limiting etc.. will add an issue
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.
i will finish making this clean up after #43686