@@ -119,7 +119,7 @@ def __init__(
119119 "blank" : "blank" ,
120120 "foot" : "foot" ,
121121 }
122- self .concatenated : StylerRenderer | None = None
122+ self .concatenated : list [ StylerRenderer ] = []
123123 # add rendering variables
124124 self .hide_index_names : bool = False
125125 self .hide_column_names : bool = False
@@ -161,27 +161,34 @@ def _render(
161161 stylers for use within `_translate_latex`
162162 """
163163 self ._compute ()
164- dx = None
165- if self .concatenated is not None :
166- self .concatenated .hide_index_ = self .hide_index_
167- self .concatenated .hidden_columns = self .hidden_columns
168- self .concatenated .css = {
164+ dxs = []
165+ ctx_len = len (self .index )
166+ for i , concatenated in enumerate (self .concatenated ):
167+ concatenated .hide_index_ = self .hide_index_
168+ concatenated .hidden_columns = self .hidden_columns
169+ foot = f"{ self .css ['foot' ]} { i } "
170+ concatenated .css = {
169171 ** self .css ,
170- "data" : f"{ self . css [ ' foot' ] } _ { self . css [ 'data' ] } " ,
171- "row_heading" : f"{ self . css [ ' foot' ] } _ { self . css [ 'row_heading' ] } " ,
172- "row" : f"{ self . css [ ' foot' ] } _ { self . css [ 'row' ] } " ,
173- "foot" : self . css [ " foot" ] ,
172+ "data" : f"{ foot } _data " ,
173+ "row_heading" : f"{ foot } _row_heading " ,
174+ "row" : f"{ foot } _row " ,
175+ "foot" : f" { foot } _foot" ,
174176 }
175- dx = self . concatenated ._render (
177+ dx = concatenated ._render (
176178 sparse_index , sparse_columns , max_rows , max_cols , blank
177179 )
180+ dxs .append (dx )
178181
179- for (r , c ), v in self . concatenated .ctx .items ():
180- self .ctx [(r + len ( self . index ) , c )] = v
181- for (r , c ), v in self . concatenated .ctx_index .items ():
182- self .ctx_index [(r + len ( self . index ) , c )] = v
182+ for (r , c ), v in concatenated .ctx .items ():
183+ self .ctx [(r + ctx_len , c )] = v
184+ for (r , c ), v in concatenated .ctx_index .items ():
185+ self .ctx_index [(r + ctx_len , c )] = v
183186
184- d = self ._translate (sparse_index , sparse_columns , max_rows , max_cols , blank , dx )
187+ ctx_len += len (concatenated .index )
188+
189+ d = self ._translate (
190+ sparse_index , sparse_columns , max_rows , max_cols , blank , dxs
191+ )
185192 return d
186193
187194 def _render_html (
@@ -258,7 +265,7 @@ def _translate(
258265 max_rows : int | None = None ,
259266 max_cols : int | None = None ,
260267 blank : str = " " ,
261- dx : dict | None = None ,
268+ dxs : list [ dict ] | None = None ,
262269 ):
263270 """
264271 Process Styler data and settings into a dict for template rendering.
@@ -278,15 +285,17 @@ def _translate(
278285 Specific max rows and cols. max_elements always take precedence in render.
279286 blank : str
280287 Entry to top-left blank cells.
281- dx : dict
282- The render dict of the concatenated Styler .
288+ dxs : list[ dict]
289+ The render dicts of the concatenated Stylers .
283290
284291 Returns
285292 -------
286293 d : dict
287294 The following structure: {uuid, table_styles, caption, head, body,
288295 cellstyle, table_attributes}
289296 """
297+ if dxs is None :
298+ dxs = []
290299 self .css ["blank_value" ] = blank
291300
292301 # construct render dict
@@ -340,10 +349,12 @@ def _translate(
340349 ]
341350 d .update ({k : map })
342351
343- if dx is not None : # self.concatenated is not None
352+ for dx in dxs : # self.concatenated is not empty
344353 d ["body" ].extend (dx ["body" ]) # type: ignore[union-attr]
345354 d ["cellstyle" ].extend (dx ["cellstyle" ]) # type: ignore[union-attr]
346- d ["cellstyle_index" ].extend (dx ["cellstyle" ]) # type: ignore[union-attr]
355+ d ["cellstyle_index" ].extend ( # type: ignore[union-attr]
356+ dx ["cellstyle_index" ]
357+ )
347358
348359 table_attr = self .table_attributes
349360 if not get_option ("styler.html.mathjax" ):
@@ -847,23 +858,27 @@ def _translate_latex(self, d: dict, clines: str | None) -> None:
847858 for r , row in enumerate (d ["head" ])
848859 ]
849860
850- def concatenated_visible_rows (obj , n , row_indices ):
861+ def _concatenated_visible_rows (obj , n , row_indices ):
851862 """
852863 Extract all visible row indices recursively from concatenated stylers.
853864 """
854865 row_indices .extend (
855866 [r + n for r in range (len (obj .index )) if r not in obj .hidden_rows ]
856867 )
857- return (
858- row_indices
859- if obj .concatenated is None
860- else concatenated_visible_rows (
861- obj .concatenated , n + len (obj .index ), row_indices
862- )
863- )
868+ n += len (obj .index )
869+ for concatenated in obj .concatenated :
870+ n = _concatenated_visible_rows (concatenated , n , row_indices )
871+ return n
872+
873+ def concatenated_visible_rows (obj ):
874+ row_indices : list [int ] = []
875+ _concatenated_visible_rows (obj , 0 , row_indices )
876+ # TODO try to consolidate the concat visible rows
877+ # methods to a single function / recursion for simplicity
878+ return row_indices
864879
865880 body = []
866- for r , row in zip (concatenated_visible_rows (self , 0 , [] ), d ["body" ]):
881+ for r , row in zip (concatenated_visible_rows (self ), d ["body" ]):
867882 # note: cannot enumerate d["body"] because rows were dropped if hidden
868883 # during _translate_body so must zip to acquire the true r-index associated
869884 # with the ctx obj which contains the cell styles.
0 commit comments