Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 64d8a19

Browse files
Documented functionality and added README
look in `proto/px_overlay/README.md` for more information.
1 parent 9919594 commit 64d8a19

File tree

3 files changed

+70
-17
lines changed

3 files changed

+70
-17
lines changed

proto/px_overlay/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# `px.overlay` prototype
2+
3+
This demonstrates one possible way of combining two figures into a single
4+
figure.
5+
6+
To see an example, run (from the root of the `plotly.py` repo):
7+
8+
```bash
9+
PYTHONPATH=proto/px_overlay python proto/px_overlay/multilayered_data_test.py
10+
```
11+
12+
To see the code that does the overlaying, start with the `px_simple_overlay`
13+
function in `proto/px_overlay/px_overlay.py`. In this function there are a few
14+
comments marked with `TODO` that indicate places for improvement in the
15+
functionality.

proto/px_overlay/multilayered_data_test.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@
33
import plotly.express as px
44
from px_overlay import px_simple_overlay
55

6+
# Demonstrates px_overlay prototype.
7+
8+
# Make some data that can be faceted by row, col and color, and split into 2
9+
# sets, which will go to the first and second figure respectively.
610
df = test_data.multilayered_data(d_divs=[2, 3, 4, 2], rwalk=0.1)
7-
print(df)
11+
12+
# The titles of the figures use the last dimension in the data. The title is
13+
# formatted "column_name=column_value", so here we extract the column name.
814
last_cat = df.columns[3]
915
figs = []
1016
for px_call, last_cat_0 in zip([px.line, px.bar], list(set(df[last_cat]))):
17+
# px_call is the chart type to make and last_cat_0 is the column_value for
18+
# that figure which is used in forming the title.
1119
df_slice = df.loc[df[last_cat] == last_cat_0]
1220
fig = px_call(
1321
df_slice,
@@ -17,20 +25,32 @@
1725
facet_col=df.columns[1],
1826
color=df.columns[2],
1927
)
28+
2029
fig.update_layout(title="%s=%s" % (last_cat, last_cat_0,))
2130
figs.append(fig)
2231

32+
# Add some annotations to make sure they are copied to the final figure properly
2333
figs[0].add_hline(y=1, row=1, col="all")
24-
figs[1].add_vline(x=10, row="all", col=2)
2534
figs[0].add_annotation(
2635
x=0.25, y=0.5, xref="x domain", yref="y domain", row=2, col=3, text="yo"
2736
)
37+
# Note that these annotations should be mapped to a secondary y axis (observe this
38+
# in the final figure by dragging their corresponding secondary y axes).
39+
figs[1].add_vline(x=10, row="all", col=2)
2840
figs[1].add_annotation(
2941
x=0.5, y=0.35, xref="x domain", yref="y", row=1, col=2, text="budday"
3042
)
43+
# Set the bar modes for both to see that the first figure that the barmode for
44+
# the final figure will be taken from the figure that has bars.
3145
figs[0].layout.barmode = "group"
3246
figs[1].layout.barmode = "relative"
47+
48+
# overlay the figures
3349
final_fig = px_simple_overlay(*figs, fig1_secondary_y=True)
50+
51+
# Show the initial figures
3452
for fig in figs:
3553
fig.show()
54+
55+
# Show the final figure
3656
final_fig.show()

proto/px_overlay/px_overlay.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,16 @@ def map_annotation_like_obj_axis(oldfig, newfig, an, force_secondary_y=False):
199199

200200
def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
201201
"""
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.
204212
"""
205213
if fig1_secondary_y and (
206214
("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):
212220
grid_ref_shape = fig_grid_ref_shape(fig0)
213221
if grid_ref_shape != fig_grid_ref_shape(fig1):
214222
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."
216224
)
217-
# reflow the colors
225+
# get colors for reflowing
218226
colorway = fig0.layout.template.layout.colorway
219227
specs = None
220228
if fig1_secondary_y:
221229
specs = [
222230
[dict(secondary_y=True) for __ in range(grid_ref_shape[1])]
223231
for _ in range(grid_ref_shape[0])
224232
]
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+
)
226238
for r, c in multi_index(*fig_grid_ref_shape(fig)):
227239
print("row,col", r + 1, c + 1)
228240
for (tr, f), color in zip(
@@ -232,6 +244,7 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
232244
for f in [fig0, fig1]
233245
]
234246
),
247+
# reflow the colors
235248
cycle(colorway),
236249
):
237250
title = f.layout.title.text
@@ -261,6 +274,8 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
261274
)
262275
for (oldfig, selector), ((newfig, secy), adder) in zip(selectors, adders):
263276
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.
264279
newann = map_annotation_like_obj_axis(
265280
oldfig, newfig, ann, force_secondary_y=secy
266281
)
@@ -270,12 +285,11 @@ def px_simple_overlay(fig0, fig1, fig1_secondary_y=False):
270285
# title will be wrong
271286
fig.layout.title = None
272287
# 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.
274291
# TODO argument to force barmode? or the user can just update it after
275292
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]
279293
return fig
280294

281295

@@ -312,21 +326,25 @@ def set_main_trace_color(tr, color):
312326

313327

314328
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+
315337
barmode = None
316338
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]
320340
except IndexError:
321341
# if no figure sets barmode, then it is not set
322342
pass
323343
return barmode
324344

325345

326-
df = test_data.aug_tips()
327-
328-
329346
def simple_overlay_example():
347+
df = test_data.aug_tips()
330348
fig0 = px.scatter(df, x="total_bill", y="tip", facet_row="sex", facet_col="smoker")
331349
fig1 = px.histogram(
332350
df, x="total_bill", y="tip", facet_row="sex", facet_col="smoker"

0 commit comments

Comments
 (0)