How to pass figure layout info into subplot

In Python,I am trying to make two subplots on a 1 x 2 grid. I define the subplot array with make_subplots; call this figure fig. The (1, 1) plot, fig1, is a surface plot and the (1, 2) plot, fig2, is a heatmap. After modifying the layouts of fig1 and fig2 to get the desired display, I am able to display them separately without problems. When I combine their traces into fig and display fig, the surface and heatmap display side-by-side as expected, but without the modified layouts; they appear with all the default layouts.

How do I pass the layout information from fig1 and fig2 into fig? I am frustrated that there isn’t a simple way to do this.

Here is working example, with more layout changes to fig1 and fig 2 than are necessary to show the problem, so sorry for the lengthy paste.

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
import numpy as np
from pprint import pprint

x = np.linspace(0.0, 3.0, 4)
y = np.linspace(0.0, 3.0, 4)
z = np.array([[1.1, 2.2, 3.3, 4.4],
             [3.2, 5.3, 6.2, 2.3],
             [1.3, 5.2, 3.2, 6.2],
             [2.1, 4.6, 3.6, 4.2]])

# Set up subplots.
fig = make_subplots(rows=1, cols=2,
                    # specs=[[{'is_3d': True}, {'is_3d': False}]],
                    specs=[[{'type': 'surface'}, {'type': 'image'}]],
                    subplot_titles=['Perspective plot',
                                    'Intensity plot'])
fig.update_layout(height=700, width=1200, title_text="Side By Side Subplots")

# Surface plot from z data.
lighting_effects = dict(ambient=0.2, roughness=0.5,
                        diffuse=0.8, fresnel=0.2, specular=0.05)
trace1 = go.Surface(z=z, x=x, y=y, colorscale='gray_r', lighting=lighting_effects)
fig1 = go.Figure(data=[trace1])
fig1.layout.scene.camera.projection.type = "orthographic" # or perspective which is default
fig1.update_layout(title='Radar Magnitude', autosize=False,
    width=650, height=650,
    margin=dict(l=20, r=20, b=20, t=20),
    scene=dict(xaxis_title='x (range)', yaxis_title='y (crossrange', zaxis_title='Magnitude'))
fig1.update_layout(scene=dict(
    xaxis=dict(ticks='outside', ticklen=10),
    yaxis=dict(ticks='outside', ticklen=10),
    zaxis=dict(ticks='outside', ticklen=10)), overwrite=False)
fig1.update_layout(coloraxis_showscale=False) # Doesn't work for surface, only imshow.
fig1.update_layout(scene=dict(
    xaxis=dict(
        backgroundcolor="rgb(245, 245, 245)",
        gridcolor="black",
        showbackground=True,
        zerolinecolor="red",
        zerolinewidth=3,
        gridwidth=2,
        tickwidth=2),
    yaxis=dict(
        backgroundcolor="rgb(245, 245, 245)",
        gridcolor="black",
        showbackground=True,
        zerolinecolor="red",
        zerolinewidth=3,
        gridwidth=2,
        tickwidth=2),
    zaxis=dict(
        backgroundcolor="rgb(245, 245, 245)",
        gridcolor="black",
        showbackground=True,
        zerolinecolor="red",
        zerolinewidth=3,
        gridwidth=2,
        tickwidth=2),)
)
fig1.update_traces(showscale=False)
fig1.show() # Displays single plot with modified layout.

# Gray-scale heatmap from z data.
Axis_Labels = dict(x = "Range", y = "Crossrange")
fig2 = px.imshow(img=z, x=x, y=y, color_continuous_scale='gray_r', 
                origin="lower",
                labels = Axis_Labels)
fig2.update_layout(coloraxis_showscale=False) # No color bar
fig2.update_xaxes(ticks="outside", ticklen=10)
fig2.update_yaxes(ticks="outside", ticklen=10)
fig2.update_xaxes(minor=dict(ticklen=6, tickcolor="black", showgrid=True))
fig2.update_yaxes(minor=dict(ticklen=6, tickcolor="black", showgrid=True))
fig2.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True) # zero axis, mirror
fig2.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True)
fig2.update_xaxes(showgrid=True, gridwidth=2, gridcolor='Black') # Doesn't
fig2.update_yaxes(showgrid=True, gridwidth=2, gridcolor='Black') # work
fig2.update_layout(width=600, height=600, title="Radar Magnitude",
                  margin=dict(l=10, r=10, b=10, t=30))
fig2.show()  # Displays single plot with modified layout.

fig.add_trace(fig1.data[0], 1, 1)
fig.add_trace(fig2.data[0], 1, 2)
fig.show() # Displays side-by-side plots with with _original_ layout, not mofidied.

The first subplot updates the contents of the 3D graph with fig.update_scenes(); the second uses a heatmap of the graph object.
I am not sure I have covered all your graph requests, so if there is anything missing, please feel free to add or modify using the same technique.

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
import numpy as np
from pprint import pprint

x = np.linspace(0.0, 3.0, 4)
y = np.linspace(0.0, 3.0, 4)
z = np.array([[1.1, 2.2, 3.3, 4.4],
             [3.2, 5.3, 6.2, 2.3],
             [1.3, 5.2, 3.2, 6.2],
             [2.1, 4.6, 3.6, 4.2]])

# Set up subplots.
fig = make_subplots(rows=1, cols=2,
                    # specs=[[{'is_3d': True}, {'is_3d': False}]],
                    specs=[[{'type': 'surface'}, {'type': 'image'}]],
                    subplot_titles=['Perspective plot',
                                    'Intensity plot'])

# Surface plot from z data.
lighting_effects = dict(ambient=0.2, roughness=0.5,
                        diffuse=0.8, fresnel=0.2, specular=0.05)
fig.add_trace(go.Surface(z=z, x=x, y=y, colorscale='gray_r', lighting=lighting_effects), row=1, col=1)

fig.update_scenes(xaxis=dict(ticks='outside', ticklen=10, title='x (range)'),
                  yaxis=dict(ticks='outside', ticklen=10, title='y (crossrange'),
                  zaxis=dict(ticks='outside', ticklen=10 ,title='Magnitude'),
                  camera_projection_type = "orthographic",
                  row=1, col=1)

fig.update_scenes(xaxis=dict(
        backgroundcolor="rgb(245, 245, 245)",
        gridcolor="black",
        showbackground=True,
        zerolinecolor="red",
        zerolinewidth=3,
        gridwidth=2,
        tickwidth=2),
    yaxis=dict(
        backgroundcolor="rgb(245, 245, 245)",
        gridcolor="black",
        showbackground=True,
        zerolinecolor="red",
        zerolinewidth=3,
        gridwidth=2,
        tickwidth=2),
    zaxis=dict(
        backgroundcolor="rgb(245, 245, 245)",
        gridcolor="black",
        showbackground=True,
        zerolinecolor="red",
        zerolinewidth=3,
        gridwidth=2,
        tickwidth=2), row=1, col=1)


# Gray-scale heatmap from z data.
#Axis_Labels = dict(x = "Range", y = "Crossrange")
fig.add_trace(go.Heatmap(z=z, colorscale='gray_r'), row=1, col=2)

fig.update_xaxes(title='Range', ticks="outside", ticklen=10, row=1, col=2)
fig.update_yaxes(title='Crossrange', ticks="outside", ticklen=10, row=1, col=2)
fig.update_xaxes(minor=dict(ticklen=6, tickcolor="black", showgrid=True), row=1, col=2)
fig.update_yaxes(minor=dict(ticklen=6, tickcolor="black", showgrid=True), row=1, col=2)
fig.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, row=1, col=2)
fig.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, row=1, col=2)

fig.update_layout(height=700, width=1200, title_text="Side By Side Subplots")
fig.show()

Thanks for that quick reply, r-beginners (he said, 5d later). That indeed solves the problem.

But I wonder why one has to use a separate API for plots when they happen to be placed into a subplot. Surely I’m not the only person to think this is klunky and 20x harder. Why can’t one design the plots as standalone entities and then just plug them into something like

make_suplots([[fig1, fig2],[fig3, fig4]])?

Do you think I should make a feature request?

2 Likes

Simply put, express includes functionality for subplots from the start. For example, subplotting on a categorical variable is accomplished by simply specifying the column names. To create arbitrarily different graphs, a subplotting function is provided, which is structured to create graphs with graph objects on the subplots. This is about all I can answer as far as I understand. For more information, see Plotly express Overview in the reference.

Thank you, r-beginners. I’ll check into that. But for now, my interest is mostly in surface plots and Express does not deal with surface plots at all.

I just did a quick check (again) of the Express link. The only subplot I saw used Dash or data frames and don’t use either of those. β€œsubplot” appears only once on the page and it is not germain AFAIKT.