Skip to content

BUG: styler render when using hide, MultiIndex and max_rows in combination #44248

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 5 commits into from
Nov 1, 2021
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.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Styler
- Naive sparsification is now possible for LaTeX without the multirow package (:issue:`43369`)
- :meth:`.Styler.to_html` omits CSSStyle rules for hidden table elements (:issue:`43619`)
- Custom CSS classes can now be directly specified without string replacement (:issue:`43686`)
- Bug where row trimming failed to reflect hidden rows (:issue:`43703`)
- Bug where row trimming failed to reflect hidden rows (:issue:`43703`, :issue:`44247`)
- Update and expand the export and use mechanics (:issue:`40675`)
- New method :meth:`.Styler.hide` added and deprecates :meth:`.Styler.hide_index` and :meth:`.Styler.hide_columns` (:issue:`43758`)

Expand Down
22 changes: 15 additions & 7 deletions pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -1230,29 +1230,37 @@ def _get_level_lengths(
return lengths

for i, lvl in enumerate(levels):
visible_row_count = 0 # used to break loop due to display trimming
for j, row in enumerate(lvl):
if j >= max_index:
# stop the loop due to display trimming
if visible_row_count > max_index:
break
if not sparsify:
# then lengths will always equal 1 since no aggregation.
if j not in hidden_elements:
lengths[(i, j)] = 1
visible_row_count += 1
elif (row is not lib.no_default) and (j not in hidden_elements):
# this element has not been sparsified so must be the start of section
last_label = j
lengths[(i, last_label)] = 1
visible_row_count += 1
elif row is not lib.no_default:
# even if its hidden, keep track of it in case
# length >1 and later elements are visible
# even if the above is hidden, keep track of it in case length > 1 and
# later elements are visible
last_label = j
lengths[(i, last_label)] = 0
elif j not in hidden_elements:
# then element must be part of sparsified section and is visible
visible_row_count += 1
if lengths[(i, last_label)] == 0:
# if the previous iteration was first-of-kind but hidden then offset
# if previous iteration was first-of-section but hidden then offset
last_label = j
lengths[(i, last_label)] = 1
else:
# else add to previous iteration
lengths[(i, last_label)] += 1
# else add to previous iteration but do not extend more than max
lengths[(i, last_label)] = min(
max_index, 1 + lengths[(i, last_label)]
)

non_zero_lengths = {
element: length for element, length in lengths.items() if length >= 1
Expand Down
23 changes: 23 additions & 0 deletions pandas/tests/io/formats/style/test_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,3 +1577,26 @@ def test_row_trimming_hide_index():
assert len(ctx["body"]) == 3
for r, val in enumerate(["3", "4", "..."]):
assert ctx["body"][r][1]["display_value"] == val


def test_row_trimming_hide_index_mi():
# gh 44247
df = DataFrame([[1], [2], [3], [4], [5]])
df.index = MultiIndex.from_product([[0], [0, 1, 2, 3, 4]])
with pd.option_context("styler.render.max_rows", 2):
ctx = df.style.hide([(0, 0), (0, 1)], axis="index")._translate(True, True)
assert len(ctx["body"]) == 3

# level 0 index headers (sparsified)
assert {"value": 0, "attributes": 'rowspan="2"', "is_visible": True}.items() <= ctx[
"body"
][0][0].items()
assert {"value": 0, "attributes": "", "is_visible": False}.items() <= ctx["body"][
1
][0].items()
assert {"value": "...", "is_visible": True}.items() <= ctx["body"][2][0].items()

for r, val in enumerate(["2", "3", "..."]):
assert ctx["body"][r][1]["display_value"] == val # level 1 index headers
for r, val in enumerate(["3", "4", "..."]):
assert ctx["body"][r][2]["display_value"] == val # data values