Search code examples
python-3.xbuttonplotlydropdownplotly-python

Plotly dropdown menus loosing mapping


I am plotting a a graph where the user can select the X and Y variables per demand. So far, when selecting the desired variables within one single dropdown works fine. However, when I switch to the other dropdown I loose all color mapping: see examples below:

Selecting the first dropdown: enter image description here

Selecting the second dropdown:

enter image description here

The code I am using is the following:

 for col in colnames:


    button_x_list.append(dict(method='update',
                        label=col,
                        visible=True,
                        args=[{'x':[df[col]]},{"xaxis":{"title":col}},{"marker.color_discrete_map":list(df["status"].map(colors))}],
                        )
                  )

    button_y_list.append(dict(method='update',
                        label=col,
                        visible=True,
                        args=[{'y':[df[col]]},{"yaxis":{"title":col}},{"marker.color_discrete_map":list(df["status"].map(colors))}],
                        )
                 )
                        
                            
    
    
button_x_dict= dict(direction="down",
            showactive=True,
            xanchor="left",
            yanchor="top",
            visible=True,
            buttons=button_x_list,
            pad={"r": 15, "t": 10},
            x=0.27,
            y=1.07,)

button_y_dict= dict(direction="down",
            showactive=True,
            xanchor="left",
            yanchor="top",
            visible=True,
            buttons=button_y_list,
            pad={"r": 15, "t": 10},
            x=0.5,
            y=1.07,)                         
                         

annotation_x = dict(text="X:", showarrow=False, x=0.25, y=1.05, xanchor="left",xref="paper",yref="paper", align="left",yanchor="top")
annotation_y = dict(text="Y:", showarrow=False, x=0.48, y=1.05, xanchor="left", xref="paper",yref="paper", align="left",yanchor="top")

fig = go.Figure()
fig = px.scatter(df, x="var1", y="var2", color=type_check,color_discrete_map=colors, title="Test"})

Solution

    • to make this a reproducible example, I've simulated data
    • fundamentally the issue is plotlty express generates a trace for each color. These then get out of step with updatemenus
    • change to graph objects lower level API and you can control generation of colors in a way that uses only one trace
    • if I have understood your data shape correctly, in it's superfluous to update colors in updatemenus. I have left it in place
    import plotly.graph_objects as go
    import pandas as pd
    import numpy as np
    
    s = 300
    colnames = list("3456")
    df = pd.DataFrame(
        {
            **{"status": np.random.choice(["A", "B", "C"], s)},
            **{c: np.random.uniform(5 + int(c), 10 + int(c), s) for c in colnames},
        }
    )
    
    button_x_list = []
    button_y_list = []
    colors = {"A": "red", "B": "blue", "C": "yellow"}
    for col in colnames:
    
        button_x_list.append(
            dict(
                method="update",
                label=col,
                visible=True,
                args=[
                    {"x": [df[col]]},
                    {"xaxis": {"title": col}},
                    {"marker":{"color": list(df["status"].map(colors))}},
                ],
            )
        )
    
        button_y_list.append(
            dict(
                method="update",
                label=col,
                visible=True,
                args=[
                    {"y": [df[col]]},
                    {"yaxis": {"title": col}},
                    {"marker":{"color": list(df["status"].map(colors))}},
                ],
            )
        )
    
    
    button_x_dict = dict(
        direction="down",
        showactive=True,
        xanchor="left",
        yanchor="top",
        visible=True,
        buttons=button_x_list,
        pad={"r": 15, "t": 10},
        x=0.27,
        y=1.07,
    )
    
    button_y_dict = dict(
        direction="down",
        showactive=True,
        xanchor="left",
        yanchor="top",
        visible=True,
        buttons=button_y_list,
        pad={"r": 15, "t": 10},
        x=0.5,
        y=1.07,
    )
    
    
    annotation_x = dict(
        text="X:",
        showarrow=False,
        x=0.25,
        y=1.05,
        xanchor="left",
        xref="paper",
        yref="paper",
        align="left",
        yanchor="top",
    )
    annotation_y = dict(
        text="Y:",
        showarrow=False,
        x=0.48,
        y=1.05,
        xanchor="left",
        xref="paper",
        yref="paper",
        align="left",
        yanchor="top",
    )
    
    
    fig = go.Figure(go.Scatter(x=df["3"], y=df["3"], mode="markers", marker={"color":df["status"].map(colors)}))
    fig.update_layout(
        updatemenus=[button_x_dict, button_y_dict], annotations=[annotation_x, annotation_y],
        title="Test",
        xaxis={"title":"3"}, yaxis={"title":"3"}
    )