1

In plotly I use a figure that contains several subplot. For the last subplot I want to be able to change the type with a dropdown menu.

enter image description here

However, the "restyle" action of the dropdown seems to be applied to the whole figure? If I use the dropdown, the other subplots disapear:

enter image description here

=> How to add a plotly control for a specific subplot?

or

=> How to tell a control do only influence the properties of a specific subplot?

Read data

import pandas as pd
​
# read in volcano database data
df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/volcano_db.csv",
    encoding="iso-8859-1",
)
​
# frequency of Country
freq = df
freq = freq.Country.value_counts().reset_index().rename(columns={"index": "x"})
​
# read in 3d volcano surface data
df_v = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv")

Initialize figure with subplots

from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(
    rows=3,
    cols=2,
    column_widths=[0.6, 0.4],
    row_heights=[0.4, 0.2, 0.4],
    specs=[
        [{"type": "scattergeo", "rowspan": 2}, {"type": "bar"}],
        [None, None],
        [None,                                 {"type": "surface"}]
    ]
)

# Add scattergeo globe map of volcano locations
scatter_geo = go.Scattergeo(
    lat=df["Latitude"],
    lon=df["Longitude"],
    mode="markers",
    hoverinfo="text",
    showlegend=False,
    marker=dict(color="crimson", size=4, opacity=0.8)
)

fig.add_trace(
    scatter_geo,
    row=1,
    col=1
)

# Add locations bar chart
bar = go.Bar(
    x=freq["x"][0:10],
    y=freq["Country"][0:10],
    marker=dict(color="crimson"),
    showlegend=False
)

fig.add_trace(
    bar,
    row=1,
    col=2
)

# Add 3d surface of volcano
surface_3d = go.Surface(
    z=df_v.values.tolist(),
    showscale=False
)

fig.add_trace(
    surface_3d,
    row=3,
    col=2
)
fig

Add controls

# Add dropdown

updatemenu = dict(
    buttons=list([
        dict(
            args=["type", "surface"],
            label="3D Surface",
            method="restyle"
        ),
        dict(
            args=["type", "heatmap"],
            label="Heatmap",
            method="restyle"
        )
    ]),
    direction="down",
    pad={"r": 10, "t": 10},
    showactive=True,  
    x=1,
    y=0.4
)

fig['layout'].update(
    updatemenus=[{}, {}, {}, {}, {}, updatemenu]
)

#Add slider

steps = []
for i in range(10):
    step = dict(
        method="update",
        args=[{"title": "Slider switched to step: " + str(i)}],  # layout attribute
    )
    steps.append(step)
    
slider = dict(
    active=10,
    currentvalue={"prefix": "Frequency: "},
    pad={"t": 50},
    steps=steps
)

fig.update_layout(
    sliders=[slider]
)

Additional styling

# Update geo subplot properties
fig.update_geos(
    projection_type="orthographic",
    landcolor="white",
    oceancolor="MidnightBlue",
    showocean=True,
    lakecolor="LightBlue"
)

# Rotate x-axis labels
fig.update_xaxes(tickangle=45)

# Set theme, margin, and annotation in layout
fig.update_layout(
    autosize=False,
    width=800,
    height=500,
    template="plotly_dark",
    margin=dict(r=10, t=25, b=40, l=60),
    scene_camera_eye=dict(x=2, y=2, z=0.3),
    annotations=[
        dict(
            text="Source: NOAA",
            showarrow=False,
            xref="paper",
            yref="paper",
            x=0,
            y=0)
    ]
)
fig.show()

1 Answer 1

1

Unfortunately, this is not a complete answer. But hopefully, what I'm about to show you will help you on your way. You see, you can specify which subplot to edit by the traces contained in that subplot. You do so by adding an integer in args() like so:

buttons=list([
    dict(
        args=["type", "surface", [2]],
        label="3D Surface",
        method="restyle"
    )

And [2] references the position of your trace in fig.data:

(Scattergeo({
     'geo': 'geo',
     'hoverinfo': 'text',
     'lat': array([ 34.5  , -23.3  ,  14.501, ...,  15.05 ,  14.02 ,  34.8  ]),
     'lon': array([ 131.6  ,  -67.62 ,  -90.876, ...,   42.18 ,   42.75 , -108.   ]),
     'marker': {'color': 'crimson', 'opacity': 0.8, 'size': 4},
     'mode': 'markers',
     'showlegend': False
 }),
 Bar({
     'marker': {'color': 'crimson'},
     'showlegend': False,
     'x': array(['United States', 'Russia', 'Indonesia', 'Japan', 'Chile', 'Ethiopia',
                 'Papua New Guinea', 'Philippines', 'Mexico', 'Iceland'], dtype=object),
     'xaxis': 'x',
     'y': array([184, 169, 136, 111,  87,  57,  54,  49,  41,  38], dtype=int64),
     'yaxis': 'y'
 }),
 Surface({
     'scene': 'scene',
     'showscale': False,

The problem is, that doing it this way in your case will trigger some very peculiar behavior: when the button is set to heatmap, the data becomes bart of the Bar figure:

enter image description here

And what's even stranger, is that it looks the way it should when you select 3D Surface again:

enter image description here

And I honestly have no idea what causes this. Take a look for yourself in the complete code snippet below and see what you can make of it. Maybe we'll be able to figure it out eventually...

Complete code:

from plotly.subplots import make_subplots
import plotly.graph_objects as go

import pandas as pd

# read in volcano database data
df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/volcano_db.csv",
    encoding="iso-8859-1",
)

# frequency of Country
freq = df
freq = freq.Country.value_counts().reset_index().rename(columns={"index": "x"})

# read in 3d volcano surface data
df_v = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv")
df_v

fig = make_subplots(
    rows=3,
    cols=2,
    column_widths=[0.6, 0.4],
    row_heights=[0.4, 0.2, 0.4],
    specs=[
        [{"type": "scattergeo", "rowspan": 2}, {"type": "bar"}],
        [None, None],
        [None,                                 {"type": "surface"}]
    ]
)

# Add scattergeo globe map of volcano locations
scatter_geo = go.Scattergeo(
    lat=df["Latitude"],
    lon=df["Longitude"],
    mode="markers",
    hoverinfo="text",
    showlegend=False,
    marker=dict(color="crimson", size=4, opacity=0.8)
)

fig.add_trace(
    scatter_geo,
    row=1,
    col=1
)

# Add locations bar chart
bar = go.Bar(
    x=freq["x"][0:10],
    y=freq["Country"][0:10],
    marker=dict(color="crimson"),
    showlegend=False
)

fig.add_trace(
    bar,
    row=1,
    col=2
)

# Add 3d surface of volcano
surface_3d = go.Surface(
    z=df_v.values.tolist(),
    showscale=False
)

fig.add_trace(
    surface_3d,
    row=3,
    col=2
)

# Add dropdown

updatemenu = dict(
    buttons=list([
        dict(
            args=["type", "surface", [2]],
            label="3D Surface",
            method="restyle"
        ),
        dict(
            args=["type", "heatmap", [2]],
            label="Heatmap",
            method="restyle"
        )
    ]),
    direction="down",
    pad={"r": 10, "t": 10},
    showactive=True,  
    x=1,
    y=0.4
)

fig['layout'].update(
    updatemenus=[{}, {}, {}, {}, {}, updatemenu]
)

#Add slider

steps = []
for i in range(10):
    step = dict(
        method="update",
        args=[{"title": "Slider switched to step: " + str(i)}],  # layout attribute
    )
    steps.append(step)
    
slider = dict(
    active=10,
    currentvalue={"prefix": "Frequency: "},
    pad={"t": 50},
    steps=steps
)

fig.update_layout(
    sliders=[slider]
)

# Update geo subplot properties
fig.update_geos(
    projection_type="orthographic",
    landcolor="white",
    oceancolor="MidnightBlue",
    showocean=True,
    lakecolor="LightBlue"
)

# Rotate x-axis labels
fig.update_xaxes(tickangle=45)

# Set theme, margin, and annotation in layout
fig.update_layout(
    autosize=False,
    width=800,
    height=500,
    template="plotly_dark",
    margin=dict(r=10, t=25, b=40, l=60),
    scene_camera_eye=dict(x=2, y=2, z=0.3),
    annotations=[
        dict(
            text="Source: NOAA",
            showarrow=False,
            xref="paper",
            yref="paper",
            x=0,
            y=0)
    ]
)
fig.show()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.