@@ -176,6 +176,8 @@ def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = "
176176 ROW_HEADING_CLASS = "row_heading"
177177 COL_HEADING_CLASS = "col_heading"
178178 INDEX_NAME_CLASS = "index_name"
179+ TRIMMED_COL_CLASS = "col_trim"
180+ TRIMMED_ROW_CLASS = "row_trim"
179181
180182 DATA_CLASS = "data"
181183 BLANK_CLASS = "blank"
@@ -188,15 +190,34 @@ def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = "
188190 "caption" : self .caption ,
189191 }
190192
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+
191198 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 ,
193206 )
194207 d .update ({"head" : head })
195208
196209 self .cellstyle_map : DefaultDict [tuple [CSSPair , ...], list [str ]] = defaultdict (
197210 list
198211 )
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+ )
200221 d .update ({"body" : body })
201222
202223 cellstyle : list [dict [str , CSSList | list [str ]]] = [
@@ -227,6 +248,8 @@ def _translate_header(
227248 index_name_class : str ,
228249 col_heading_class : str ,
229250 sparsify_cols : bool ,
251+ max_cols : int ,
252+ trimmed_col_class : str ,
230253 ):
231254 """
232255 Build each <tr> within table <head> as a list
@@ -252,6 +275,10 @@ def _translate_header(
252275 CSS class added to elements within the column_names section of structure.
253276 sparsify_cols : bool
254277 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.
255282
256283 Returns
257284 -------
@@ -260,10 +287,10 @@ def _translate_header(
260287 """
261288 # for sparsifying a MultiIndex
262289 col_lengths = _get_level_lengths (
263- self .columns , sparsify_cols , self .hidden_columns
290+ self .columns , sparsify_cols , max_cols , self .hidden_columns
264291 )
265292
266- clabels = self .data .columns .tolist ()
293+ clabels = self .data .columns .tolist ()[: max_cols ] # slice to allow trimming
267294 if self .data .columns .nlevels == 1 :
268295 clabels = [[x ] for x in clabels ]
269296 clabels = list (zip (* clabels ))
@@ -300,6 +327,18 @@ def _translate_header(
300327 )
301328 for c , value in enumerate (clabels [r ])
302329 ]
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+ )
303342 head .append (index_blanks + column_name + column_headers )
304343
305344 # 2) index names
@@ -318,21 +357,33 @@ def _translate_header(
318357 for c , name in enumerate (self .data .index .names )
319358 ]
320359
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+
321365 column_blanks = [
322366 _element (
323367 "th" ,
324368 f"{ blank_class } col{ c } " ,
325369 blank_value ,
326370 c not in self .hidden_columns ,
327371 )
328- for c in range (len ( clabels [ 0 ]) )
372+ for c in range (blank_len )
329373 ]
330374 head .append (index_names + column_blanks )
331375
332376 return head
333377
334378 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 ,
336387 ):
337388 """
338389 Build each <tr> within table <body> as a list
@@ -360,14 +411,52 @@ def _translate_body(
360411 The associated HTML elements needed for template rendering.
361412 """
362413 # 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 )
364415
365- rlabels = self .data .index .tolist ()
416+ rlabels = self .data .index .tolist ()[: max_rows ] # slice to allow trimming
366417 if self .data .index .nlevels == 1 :
367418 rlabels = [[x ] for x in rlabels ]
368419
369420 body = []
370421 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+
371460 index_headers = [
372461 _element (
373462 "th" ,
@@ -386,6 +475,18 @@ def _translate_body(
386475
387476 data = []
388477 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+
389490 # add custom classes from cell context
390491 cls = ""
391492 if (r , c ) in self .cell_context :
@@ -655,8 +756,40 @@ def _element(
655756 }
656757
657758
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+
658788def _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 ,
660793):
661794 """
662795 Given an index, find the level length for each element.
@@ -667,6 +800,8 @@ def _get_level_lengths(
667800 Index or columns to determine lengths of each element
668801 sparsify : bool
669802 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
670805 hidden_elements : sequence of int
671806 Index positions of elements hidden from display in the index affecting
672807 length
@@ -693,6 +828,9 @@ def _get_level_lengths(
693828
694829 for i , lvl in enumerate (levels ):
695830 for j , row in enumerate (lvl ):
831+ if j >= max_index :
832+ # stop the loop due to display trimming
833+ break
696834 if not sparsify :
697835 lengths [(i , j )] = 1
698836 elif (row is not lib .no_default ) and (j not in hidden_elements ):
0 commit comments