Skip to content

Commit 4ec6925

Browse files
authored
REF: add internal mechanics to separate index/columns sparsification in Styler (#41391)
1 parent ea05559 commit 4ec6925

File tree

2 files changed

+127
-18
lines changed

2 files changed

+127
-18
lines changed

pandas/io/formats/style_render.py

+95-16
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,33 @@ def _compute(self):
132132
r = func(self)(*args, **kwargs)
133133
return r
134134

135-
def _translate(self):
135+
def _translate(
136+
self, sparsify_index: bool | None = None, sparsify_cols: bool | None = None
137+
):
136138
"""
137-
Convert the DataFrame in `self.data` and the attrs from `_build_styles`
138-
into a dictionary of {head, body, uuid, cellstyle}.
139+
Process Styler data and settings into a dict for template rendering.
140+
141+
Convert data and settings from ``Styler`` attributes such as ``self.data``,
142+
``self.tooltips`` including applying any methods in ``self._todo``.
143+
144+
Parameters
145+
----------
146+
sparsify_index : bool, optional
147+
Whether to sparsify the index or print all hierarchical index elements
148+
sparsify_cols : bool, optional
149+
Whether to sparsify the columns or print all hierarchical column elements
150+
151+
Returns
152+
-------
153+
d : dict
154+
The following structure: {uuid, table_styles, caption, head, body,
155+
cellstyle, table_attributes}
139156
"""
157+
if sparsify_index is None:
158+
sparsify_index = get_option("display.multi_sparse")
159+
if sparsify_cols is None:
160+
sparsify_cols = get_option("display.multi_sparse")
161+
140162
ROW_HEADING_CLASS = "row_heading"
141163
COL_HEADING_CLASS = "col_heading"
142164
INDEX_NAME_CLASS = "index_name"
@@ -153,14 +175,14 @@ def _translate(self):
153175
}
154176

155177
head = self._translate_header(
156-
BLANK_CLASS, BLANK_VALUE, INDEX_NAME_CLASS, COL_HEADING_CLASS
178+
BLANK_CLASS, BLANK_VALUE, INDEX_NAME_CLASS, COL_HEADING_CLASS, sparsify_cols
157179
)
158180
d.update({"head": head})
159181

160182
self.cellstyle_map: DefaultDict[tuple[CSSPair, ...], list[str]] = defaultdict(
161183
list
162184
)
163-
body = self._translate_body(DATA_CLASS, ROW_HEADING_CLASS)
185+
body = self._translate_body(DATA_CLASS, ROW_HEADING_CLASS, sparsify_index)
164186
d.update({"body": body})
165187

166188
cellstyle: list[dict[str, CSSList | list[str]]] = [
@@ -185,20 +207,47 @@ def _translate(self):
185207
return d
186208

187209
def _translate_header(
188-
self, blank_class, blank_value, index_name_class, col_heading_class
210+
self,
211+
blank_class: str,
212+
blank_value: str,
213+
index_name_class: str,
214+
col_heading_class: str,
215+
sparsify_cols: bool,
189216
):
190217
"""
191-
Build each <tr> within table <head>, using the structure:
218+
Build each <tr> within table <head> as a list
219+
220+
Using the structure:
192221
+----------------------------+---------------+---------------------------+
193222
| index_blanks ... | column_name_0 | column_headers (level_0) |
194223
1) | .. | .. | .. |
195224
| index_blanks ... | column_name_n | column_headers (level_n) |
196225
+----------------------------+---------------+---------------------------+
197226
2) | index_names (level_0 to level_n) ... | column_blanks ... |
198227
+----------------------------+---------------+---------------------------+
228+
229+
Parameters
230+
----------
231+
blank_class : str
232+
CSS class added to elements within blank sections of the structure.
233+
blank_value : str
234+
HTML display value given to elements within blank sections of the structure.
235+
index_name_class : str
236+
CSS class added to elements within the index_names section of the structure.
237+
col_heading_class : str
238+
CSS class added to elements within the column_names section of structure.
239+
sparsify_cols : bool
240+
Whether column_headers section will add colspan attributes (>1) to elements.
241+
242+
Returns
243+
-------
244+
head : list
245+
The associated HTML elements needed for template rendering.
199246
"""
200247
# for sparsifying a MultiIndex
201-
col_lengths = _get_level_lengths(self.columns, self.hidden_columns)
248+
col_lengths = _get_level_lengths(
249+
self.columns, sparsify_cols, self.hidden_columns
250+
)
202251

203252
clabels = self.data.columns.tolist()
204253
if self.data.columns.nlevels == 1:
@@ -268,18 +317,36 @@ def _translate_header(
268317

269318
return head
270319

271-
def _translate_body(self, data_class, row_heading_class):
320+
def _translate_body(
321+
self, data_class: str, row_heading_class: str, sparsify_index: bool
322+
):
272323
"""
273-
Build each <tr> in table <body> in the following format:
324+
Build each <tr> within table <body> as a list
325+
326+
Use the following structure:
274327
+--------------------------------------------+---------------------------+
275328
| index_header_0 ... index_header_n | data_by_column |
276329
+--------------------------------------------+---------------------------+
277330
278331
Also add elements to the cellstyle_map for more efficient grouped elements in
279332
<style></style> block
333+
334+
Parameters
335+
----------
336+
data_class : str
337+
CSS class added to elements within data_by_column sections of the structure.
338+
row_heading_class : str
339+
CSS class added to elements within the index_header section of structure.
340+
sparsify_index : bool
341+
Whether index_headers section will add rowspan attributes (>1) to elements.
342+
343+
Returns
344+
-------
345+
body : list
346+
The associated HTML elements needed for template rendering.
280347
"""
281348
# for sparsifying a MultiIndex
282-
idx_lengths = _get_level_lengths(self.index)
349+
idx_lengths = _get_level_lengths(self.index, sparsify_index)
283350

284351
rlabels = self.data.index.tolist()
285352
if self.data.index.nlevels == 1:
@@ -520,14 +587,26 @@ def _element(
520587
}
521588

522589

523-
def _get_level_lengths(index, hidden_elements=None):
590+
def _get_level_lengths(
591+
index: Index, sparsify: bool, hidden_elements: Sequence[int] | None = None
592+
):
524593
"""
525594
Given an index, find the level length for each element.
526595
527-
Optional argument is a list of index positions which
528-
should not be visible.
596+
Parameters
597+
----------
598+
index : Index
599+
Index or columns to determine lengths of each element
600+
sparsify : bool
601+
Whether to hide or show each distinct element in a MultiIndex
602+
hidden_elements : sequence of int
603+
Index positions of elements hidden from display in the index affecting
604+
length
529605
530-
Result is a dictionary of (level, initial_position): span
606+
Returns
607+
-------
608+
Dict :
609+
Result is a dictionary of (level, initial_position): span
531610
"""
532611
if isinstance(index, MultiIndex):
533612
levels = index.format(sparsify=lib.no_default, adjoin=False)
@@ -546,7 +625,7 @@ def _get_level_lengths(index, hidden_elements=None):
546625

547626
for i, lvl in enumerate(levels):
548627
for j, row in enumerate(lvl):
549-
if not get_option("display.multi_sparse"):
628+
if not sparsify:
550629
lengths[(i, j)] = 1
551630
elif (row is not lib.no_default) and (j not in hidden_elements):
552631
last_label = j

pandas/tests/io/formats/style/test_style.py

+32-2
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,24 @@ def test_get_level_lengths(self):
844844
(1, 4): 1,
845845
(1, 5): 1,
846846
}
847-
result = _get_level_lengths(index)
847+
result = _get_level_lengths(index, sparsify=True)
848+
tm.assert_dict_equal(result, expected)
849+
850+
expected = {
851+
(0, 0): 1,
852+
(0, 1): 1,
853+
(0, 2): 1,
854+
(0, 3): 1,
855+
(0, 4): 1,
856+
(0, 5): 1,
857+
(1, 0): 1,
858+
(1, 1): 1,
859+
(1, 2): 1,
860+
(1, 3): 1,
861+
(1, 4): 1,
862+
(1, 5): 1,
863+
}
864+
result = _get_level_lengths(index, sparsify=False)
848865
tm.assert_dict_equal(result, expected)
849866

850867
def test_get_level_lengths_un_sorted(self):
@@ -858,7 +875,20 @@ def test_get_level_lengths_un_sorted(self):
858875
(1, 2): 1,
859876
(1, 3): 1,
860877
}
861-
result = _get_level_lengths(index)
878+
result = _get_level_lengths(index, sparsify=True)
879+
tm.assert_dict_equal(result, expected)
880+
881+
expected = {
882+
(0, 0): 1,
883+
(0, 1): 1,
884+
(0, 2): 1,
885+
(0, 3): 1,
886+
(1, 0): 1,
887+
(1, 1): 1,
888+
(1, 2): 1,
889+
(1, 3): 1,
890+
}
891+
result = _get_level_lengths(index, sparsify=False)
862892
tm.assert_dict_equal(result, expected)
863893

864894
def test_mi_sparse(self):

0 commit comments

Comments
 (0)