@vincentm
Changing column_widths
implies a fig.layout_update
, i.e. for each subplot cell of spec type ‘xy’ or ‘scene’, must be updated the corresponding xaxis domain (for ‘xy’ type), respectively the correspondin scene, x domain. The range of each domain is computed taking into account a few of make_subplots
settings . The custom function new_xdomains()
, defined below, computes the new ranges for these domains. It is defined such that to be used in subplots like yours, i.e. subplots with specs of type in [‘xy’, ‘scene’], or specs type, the trace name, but not in [‘polar’, ‘ternary’, ‘mapbox’, ‘domain’]. Also no cell pads are allowed in specs definition, and are considered only the default settings:
column_titles=None,
row_titles=None,
x_title=None,
y_title=None`
Any modified settings among those listed above, involves a special treatment.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
def new_xdomains(rows, cols, col_w, horizontal_spacing, specs=None):
#rows, cols -integers passed to make_subplots()
#col_w - list of new column widths
#specs - None or list of specs passed to make_subplots()
#returns new xaxis domain for each subplot cell, corresponding to the new column_width, i.e. col_w
if specs is None:
specs = [[{} for c in range(cols)] for r in range(rows)]
else:
if len(specs) != rows or len(specs[0]) != cols:
raise ValueError('bad length of the list specs')
else:
for srow in specs:
for spec in srow:
if spec['type'] in ['polar','ternary','mapbox', 'domain']:
raise ValueError('this function works only for spec type "xy", and "scene"')
else: continue
if len(col_w) != cols:
raise ValueError('your list of new column widths must have the length f"{col}"')
else:
cs = float(sum(col_w))
xd_widths=[] #actual lengths of new columns,ie taking into account horizontal_spacing, too
for w in col_w:
xd_widths.append((1 - horizontal_spacing * (cols - 1)) * (w/cs))
#these settings for col_list and row_list are valid when subplot cells
#are indexed as matrices, i.e (1,1) is the upper left cell
col_list=range(cols)[::1]
row_list=range(rows)[::-1]
left_xdom = [[sum(xd_widths[:c]) + c * horizontal_spacing for c in col_list] for r in row_list]
xds= []
for r, spec_row in enumerate(specs):
for c, spec in enumerate(spec_row):
if spec is None:
continue
xl = left_xdom[r][c]
xr = left_xdom[r][c] + xd_widths[c]
xds.append([xl, xr])
return xds #xds is the list of x domains for the cells enumerated line by line (in our example (1,1), (1,2), (2,1), (2,2)
Let us give a general example of subplot with admissible specs:
#vars whose values are used to set keywords in make_subplots and to are passed to the function new_xdomain
specs=[[{"type": "xy"}, {"type": "heatmap"}],
[{"type": "scene"}, {}]]
rows=2
cols = 2
horizontal_spacing=0.05
#end vars definitions
fig = make_subplots(subplot_titles=("Title (1,1)", "Title (1,2)", "Title (2,1)(3D)", "Title (2,2)"),
rows=rows, cols=cols,
column_widths=[.4,.6],
specs=specs,
horizontal_spacing=horizontal_spacing,
vertical_spacing=0.08
)
print(fig.layout) #print the initial layout to decide what we'll update
fig.add_trace(go.Scatter(x=[1,2,3], y=[-1 , 1.35, 0.7], mode="lines"), 1,1)
fig.add_trace(go.Heatmap(z=np.random.randint(2, 13, (5,5)), showscale=False), 1, 2)
fig.add_trace(go.Surface(x=[1,2,3,4], y=[2,3,4], z= np.random.randint(1, 15, (3, 4)), showscale=False), 2, 1)
fig.add_trace(go.Scatter(x=2*np.random.rand(8), y=1+3*np.random.rand(8), mode="markers"), 2,2)
fig.update_layout(width=770, height=600, showlegend=False)
fig.update_annotations(font_size=12)
This is the initial subplot fig:
new_column_widths = [0.55, 0.45]
xd_lims=new_xdomains(rows, cols, new_column_widths, horizontal_spacing, specs=specs)
print(xd_lims)
fig.update_layout(xaxis_domain= xd_lims[0],
scene_domain_x=xd_lims[2])
fig.update_layout({'xaxis2': {'domain': xd_lims[1]},
'xaxis3': {'domain': xd_lims[3]}
})
The new figure after changing column_widths
by new_column_widths
:
Conclusion: We cannot update column_widths
via fig.update_traces()
, but via fig.update_layout()
after calling the above auxilliary function, that returns the new xaxes domains.