@@ -199,8 +199,16 @@ def map_annotation_like_obj_axis(oldfig, newfig, an, force_secondary_y=False):
199
199
200
200
def px_simple_overlay (fig0 , fig1 , fig1_secondary_y = False ):
201
201
"""
202
- Combines two figures by just using the layout of the first figure and
203
- appending the data of the second figure.
202
+ Combines two figures by putting all the traces from fig0 and fig1 on a new
203
+ figure (fig). Then the annotation-like objects are copied to fig (i.e., the
204
+ titles are not copied).
205
+ The colors are reassigned so each trace has a unique color until all the
206
+ colors in the colorway are exhausted and then loops through the colorway to
207
+ assign additional colors (this is referred to as "reflowing" below).
208
+ In order to differentiate the traces in the legend, if fig0 or fig1 have
209
+ titles, they are prepended to the trace name.
210
+ If fig1_secondary_y is True, then the yaxes from fig1 are placed on
211
+ secondary y axes in the new figure.
204
212
"""
205
213
if fig1_secondary_y and (
206
214
("px" not in fig0 ._aux .keys ()) or ("px" not in fig0 ._aux .keys ())
@@ -212,17 +220,21 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
212
220
grid_ref_shape = fig_grid_ref_shape (fig0 )
213
221
if grid_ref_shape != fig_grid_ref_shape (fig1 ):
214
222
raise ValueError (
215
- "Only two figures with the same subplot geometry can be overlayd ."
223
+ "Only two figures with the same subplot geometry can be overlayed ."
216
224
)
217
- # reflow the colors
225
+ # get colors for reflowing
218
226
colorway = fig0 .layout .template .layout .colorway
219
227
specs = None
220
228
if fig1_secondary_y :
221
229
specs = [
222
230
[dict (secondary_y = True ) for __ in range (grid_ref_shape [1 ])]
223
231
for _ in range (grid_ref_shape [0 ])
224
232
]
225
- fig = make_subplots (* fig_grid_ref_shape (fig0 ), specs = specs )
233
+ # TODO: This needs to detect the start_cell of the input figures rather than
234
+ # assuming 'bottom-left', which is just the px default start_cell
235
+ fig = make_subplots (
236
+ * fig_grid_ref_shape (fig0 ), specs = specs , start_cell = "bottom-left"
237
+ )
226
238
for r , c in multi_index (* fig_grid_ref_shape (fig )):
227
239
print ("row,col" , r + 1 , c + 1 )
228
240
for (tr , f ), color in zip (
@@ -232,6 +244,7 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
232
244
for f in [fig0 , fig1 ]
233
245
]
234
246
),
247
+ # reflow the colors
235
248
cycle (colorway ),
236
249
):
237
250
title = f .layout .title .text
@@ -261,6 +274,8 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
261
274
)
262
275
for (oldfig , selector ), ((newfig , secy ), adder ) in zip (selectors , adders ):
263
276
for ann in selector (oldfig ):
277
+ # TODO this function needs to eventually take into consideration the
278
+ # start_cell arguments of the figures involved in the mapping.
264
279
newann = map_annotation_like_obj_axis (
265
280
oldfig , newfig , ann , force_secondary_y = secy
266
281
)
@@ -270,12 +285,11 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
270
285
# title will be wrong
271
286
fig .layout .title = None
272
287
# preserve bar mode
273
- # if both figures have barmode set, the first is taken, otherwise the set one is taken
288
+ # if both figures have barmode set, the first is taken from the figure that
289
+ # has bars (so just the one from fig0 if both have bars), otherwise the set
290
+ # one is taken.
274
291
# TODO argument to force barmode? or the user can just update it after
275
292
fig .layout .barmode = get_first_set_barmode ([fig0 , fig1 ])
276
- # also include annotations, shapes and layout images from fig1
277
- for kw in ["annotations" , "shapes" , "images" ]:
278
- fig .layout [kw ] += fig1 .layout [kw ]
279
293
return fig
280
294
281
295
@@ -312,21 +326,25 @@ def set_main_trace_color(tr, color):
312
326
313
327
314
328
def get_first_set_barmode (figs ):
329
+ """ Get first bar mode from the figure that has it set and has bar traces. """
330
+
331
+ def _bar_mode_filter (f ):
332
+ return (
333
+ any ([type (tr ) == type (go .Bar ()) for tr in f .data ])
334
+ and f .layout .barmode is not None
335
+ )
336
+
315
337
barmode = None
316
338
try :
317
- barmode = list (
318
- filter (lambda x : x is not None , [f .layout .barmode for f in figs ])
319
- )[0 ]
339
+ barmode = [f .layout .barmode for f in filter (_bar_mode_filter , figs )][0 ]
320
340
except IndexError :
321
341
# if no figure sets barmode, then it is not set
322
342
pass
323
343
return barmode
324
344
325
345
326
- df = test_data .aug_tips ()
327
-
328
-
329
346
def simple_overlay_example ():
347
+ df = test_data .aug_tips ()
330
348
fig0 = px .scatter (df , x = "total_bill" , y = "tip" , facet_row = "sex" , facet_col = "smoker" )
331
349
fig1 = px .histogram (
332
350
df , x = "total_bill" , y = "tip" , facet_row = "sex" , facet_col = "smoker"
0 commit comments