@@ -176,6 +176,8 @@ def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = "
176
176
ROW_HEADING_CLASS = "row_heading"
177
177
COL_HEADING_CLASS = "col_heading"
178
178
INDEX_NAME_CLASS = "index_name"
179
+ TRIMMED_COL_CLASS = "col_trim"
180
+ TRIMMED_ROW_CLASS = "row_trim"
179
181
180
182
DATA_CLASS = "data"
181
183
BLANK_CLASS = "blank"
@@ -188,15 +190,34 @@ def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = "
188
190
"caption" : self .caption ,
189
191
}
190
192
193
+ max_elements = get_option ("styler.render.max_elements" )
194
+ max_rows , max_cols = _get_trimming_maximums (
195
+ len (self .data .index ), len (self .data .columns ), max_elements
196
+ )
197
+
191
198
head = self ._translate_header (
192
- BLANK_CLASS , BLANK_VALUE , INDEX_NAME_CLASS , COL_HEADING_CLASS , sparse_cols
199
+ BLANK_CLASS ,
200
+ BLANK_VALUE ,
201
+ INDEX_NAME_CLASS ,
202
+ COL_HEADING_CLASS ,
203
+ sparse_cols ,
204
+ max_cols ,
205
+ TRIMMED_COL_CLASS ,
193
206
)
194
207
d .update ({"head" : head })
195
208
196
209
self .cellstyle_map : DefaultDict [tuple [CSSPair , ...], list [str ]] = defaultdict (
197
210
list
198
211
)
199
- body = self ._translate_body (DATA_CLASS , ROW_HEADING_CLASS , sparse_index )
212
+ body = self ._translate_body (
213
+ DATA_CLASS ,
214
+ ROW_HEADING_CLASS ,
215
+ sparse_index ,
216
+ max_rows ,
217
+ max_cols ,
218
+ TRIMMED_ROW_CLASS ,
219
+ TRIMMED_COL_CLASS ,
220
+ )
200
221
d .update ({"body" : body })
201
222
202
223
cellstyle : list [dict [str , CSSList | list [str ]]] = [
@@ -227,6 +248,8 @@ def _translate_header(
227
248
index_name_class : str ,
228
249
col_heading_class : str ,
229
250
sparsify_cols : bool ,
251
+ max_cols : int ,
252
+ trimmed_col_class : str ,
230
253
):
231
254
"""
232
255
Build each <tr> within table <head> as a list
@@ -252,6 +275,10 @@ def _translate_header(
252
275
CSS class added to elements within the column_names section of structure.
253
276
sparsify_cols : bool
254
277
Whether column_headers section will add colspan attributes (>1) to elements.
278
+ max_cols : int
279
+ Maximum number of columns to render. If exceeded will contain `...` filler.
280
+ trimmed_col_class : str
281
+ CSS class added to elements within a column including `...` trimmed vals.
255
282
256
283
Returns
257
284
-------
@@ -260,10 +287,10 @@ def _translate_header(
260
287
"""
261
288
# for sparsifying a MultiIndex
262
289
col_lengths = _get_level_lengths (
263
- self .columns , sparsify_cols , self .hidden_columns
290
+ self .columns , sparsify_cols , max_cols , self .hidden_columns
264
291
)
265
292
266
- clabels = self .data .columns .tolist ()
293
+ clabels = self .data .columns .tolist ()[: max_cols ] # slice to allow trimming
267
294
if self .data .columns .nlevels == 1 :
268
295
clabels = [[x ] for x in clabels ]
269
296
clabels = list (zip (* clabels ))
@@ -300,6 +327,18 @@ def _translate_header(
300
327
)
301
328
for c , value in enumerate (clabels [r ])
302
329
]
330
+
331
+ if len (self .data .columns ) > max_cols :
332
+ # add an extra column with `...` value to indicate trimming
333
+ column_headers .append (
334
+ _element (
335
+ "th" ,
336
+ f"{ col_heading_class } level{ r } { trimmed_col_class } " ,
337
+ "..." ,
338
+ True ,
339
+ attributes = "" ,
340
+ )
341
+ )
303
342
head .append (index_blanks + column_name + column_headers )
304
343
305
344
# 2) index names
@@ -318,21 +357,33 @@ def _translate_header(
318
357
for c , name in enumerate (self .data .index .names )
319
358
]
320
359
360
+ if len (self .data .columns ) <= max_cols :
361
+ blank_len = len (clabels [0 ])
362
+ else :
363
+ blank_len = len (clabels [0 ]) + 1 # to allow room for `...` trim col
364
+
321
365
column_blanks = [
322
366
_element (
323
367
"th" ,
324
368
f"{ blank_class } col{ c } " ,
325
369
blank_value ,
326
370
c not in self .hidden_columns ,
327
371
)
328
- for c in range (len ( clabels [ 0 ]) )
372
+ for c in range (blank_len )
329
373
]
330
374
head .append (index_names + column_blanks )
331
375
332
376
return head
333
377
334
378
def _translate_body (
335
- self , data_class : str , row_heading_class : str , sparsify_index : bool
379
+ self ,
380
+ data_class : str ,
381
+ row_heading_class : str ,
382
+ sparsify_index : bool ,
383
+ max_rows : int ,
384
+ max_cols : int ,
385
+ trimmed_row_class : str ,
386
+ trimmed_col_class : str ,
336
387
):
337
388
"""
338
389
Build each <tr> within table <body> as a list
@@ -360,14 +411,52 @@ def _translate_body(
360
411
The associated HTML elements needed for template rendering.
361
412
"""
362
413
# for sparsifying a MultiIndex
363
- idx_lengths = _get_level_lengths (self .index , sparsify_index )
414
+ idx_lengths = _get_level_lengths (self .index , sparsify_index , max_rows )
364
415
365
- rlabels = self .data .index .tolist ()
416
+ rlabels = self .data .index .tolist ()[: max_rows ] # slice to allow trimming
366
417
if self .data .index .nlevels == 1 :
367
418
rlabels = [[x ] for x in rlabels ]
368
419
369
420
body = []
370
421
for r , row_tup in enumerate (self .data .itertuples ()):
422
+ if r >= max_rows : # used only to add a '...' trimmed row:
423
+ index_headers = [
424
+ _element (
425
+ "th" ,
426
+ f"{ row_heading_class } level{ c } { trimmed_row_class } " ,
427
+ "..." ,
428
+ not self .hidden_index ,
429
+ attributes = "" ,
430
+ )
431
+ for c in range (self .data .index .nlevels )
432
+ ]
433
+
434
+ data = [
435
+ _element (
436
+ "td" ,
437
+ f"{ data_class } col{ c } { trimmed_row_class } " ,
438
+ "..." ,
439
+ (c not in self .hidden_columns ),
440
+ attributes = "" ,
441
+ )
442
+ for c in range (max_cols )
443
+ ]
444
+
445
+ if len (self .data .columns ) > max_cols :
446
+ # columns are also trimmed so we add the final element
447
+ data .append (
448
+ _element (
449
+ "td" ,
450
+ f"{ data_class } { trimmed_row_class } { trimmed_col_class } " ,
451
+ "..." ,
452
+ True ,
453
+ attributes = "" ,
454
+ )
455
+ )
456
+
457
+ body .append (index_headers + data )
458
+ break
459
+
371
460
index_headers = [
372
461
_element (
373
462
"th" ,
@@ -386,6 +475,18 @@ def _translate_body(
386
475
387
476
data = []
388
477
for c , value in enumerate (row_tup [1 :]):
478
+ if c >= max_cols :
479
+ data .append (
480
+ _element (
481
+ "td" ,
482
+ f"{ data_class } row{ r } { trimmed_col_class } " ,
483
+ "..." ,
484
+ True ,
485
+ attributes = "" ,
486
+ )
487
+ )
488
+ break
489
+
389
490
# add custom classes from cell context
390
491
cls = ""
391
492
if (r , c ) in self .cell_context :
@@ -655,8 +756,40 @@ def _element(
655
756
}
656
757
657
758
759
+ def _get_trimming_maximums (rn , cn , max_elements , scaling_factor = 0.8 ):
760
+ """
761
+ Recursively reduce the number of rows and columns to satisfy max elements.
762
+
763
+ Parameters
764
+ ----------
765
+ rn, cn : int
766
+ The number of input rows / columns
767
+ max_elements : int
768
+ The number of allowable elements
769
+
770
+ Returns
771
+ -------
772
+ rn, cn : tuple
773
+ New rn and cn values that satisfy the max_elements constraint
774
+ """
775
+
776
+ def scale_down (rn , cn ):
777
+ if cn >= rn :
778
+ return rn , int (cn * scaling_factor )
779
+ else :
780
+ return int (rn * scaling_factor ), cn
781
+
782
+ while rn * cn > max_elements :
783
+ rn , cn = scale_down (rn , cn )
784
+
785
+ return rn , cn
786
+
787
+
658
788
def _get_level_lengths (
659
- index : Index , sparsify : bool , hidden_elements : Sequence [int ] | None = None
789
+ index : Index ,
790
+ sparsify : bool ,
791
+ max_index : int ,
792
+ hidden_elements : Sequence [int ] | None = None ,
660
793
):
661
794
"""
662
795
Given an index, find the level length for each element.
@@ -667,6 +800,8 @@ def _get_level_lengths(
667
800
Index or columns to determine lengths of each element
668
801
sparsify : bool
669
802
Whether to hide or show each distinct element in a MultiIndex
803
+ max_index : int
804
+ The maximum number of elements to analyse along the index due to trimming
670
805
hidden_elements : sequence of int
671
806
Index positions of elements hidden from display in the index affecting
672
807
length
@@ -693,6 +828,9 @@ def _get_level_lengths(
693
828
694
829
for i , lvl in enumerate (levels ):
695
830
for j , row in enumerate (lvl ):
831
+ if j >= max_index :
832
+ # stop the loop due to display trimming
833
+ break
696
834
if not sparsify :
697
835
lengths [(i , j )] = 1
698
836
elif (row is not lib .no_default ) and (j not in hidden_elements ):
0 commit comments