'Plotly: Show color legend on scatter plot with dropdown menu and discrete marker colors

Long post - I'm new to python and plotly, so appreciate the help!

I'm building a scatter plot with plotly using bike ride/workout data coming from a pandas dataframe with summary metrics (output/work, resistance, cadence/speed, etc.) as well as the length/duration for each ride. I'm trying to build in two features in particular into the scatter plot: 1) a dropdown menu to change/toggle the data displayed on the y-axis between 4 metrics fields (Total Output, Average Output, Average Cadence, and Average Resistance) and 2) a legend that represents data values mapped to discrete colors, in this case the ride.duration (each record/ride is either 5, 10, 20, 30, 45, or 60 min) and haven't been able to get both of them to do what I want at the same time.

I've been able to build a scatter plot with a dynamic y-axis dropdown and color the points according to the ride duration, but I can't get the ride duration legend to show up to indicate the color to duration mappings.

My code is below:


#test dataset - this is just a couple of points, so it won't look like the screenshots I've provided but should hopefully do the job!
all_rides_clean = pd.DataFrame(np.array([['a2662', '2022-04-24 18:09:25', 145,120,78,42,20,"Ben"], 
                                        ['b10e', '2022-03-21 11:28:20', 128,71,66,35,30,"Emma"], 
                                        ['850g', '2021-01-19 11:29:31',121,101,80,38,45,"Leanne"],
                                        ['897b','2021-09-21 22:46:30',175,65,54,37,60,"Emma"]]),
                            columns=['id','created_at','Total Output','Avg Output','Avg Cadence','Average Resistance','ride.duration',
                                    'instructor.name'])
all_ride[['Total Output','Avg Output','Avg Cadence','Avg Resistance']] = all_ride[['Total Output','Avg Output','Avg Cadence','Avg Resistance']].apply(pd.to_numeric)

fig = go.Figure()

# dynamic y-axis dropdown labels
metrics = ['Total Output','Avg Output','Avg Cadence','Avg Resistance']

# make list of default plotly colors in hex
plotly_colors=[
                '#1f77b4',  # muted blue
                '#ff7f0e',  # safety orange
                '#2ca02c',  # cooked asparagus green
                '#d62728',  # brick red
                '#9467bd',  # muted purple
                '#8c564b',  # chestnut brown
                '#e377c2',  # raspberry yogurt pink
                '#7f7f7f',  # middle gray
                '#bcbd22',  # curry yellow-green
                '#17becf'   # blue-teal
              ]

# create dictionary to associate colors with unique categories
color_dict = dict(zip(all_rides_clean['ride.duration'].unique(),plotly_colors))

# map new column with hex colors to pass to go.Scatter()
all_rides_clean['hex']= all_rides_clean['ride.duration'].map(color_dict)

for column in metrics:
    fig.add_trace(
        go.Scatter(
            x = all_rides_clean['created_at'],
            y = all_rides_clean[column],
            name = column,
            mode = 'markers',
            #setting color legend
            marker=dict(color=all_rides_clean['hex']),
            showlegend=False
        )
    )
    
fig.update_layout(
    updatemenus=[go.layout.Updatemenu(
        active=0,
        buttons=list(
            [dict(label = 'Total Output',
                  method = 'update',
                  args = [{'visible': [True, False, False, False]}, # the index of True aligns with the indices of plot traces
                          {'title': 'Total Output',
                           'showlegend':False}]),
             dict(label = 'Avg Output',
                  method = 'update',
                  args = [{'visible': [False, True, False, False]},
                          {'title': 'Avg Output',
                           'showlegend':False}]),
             dict(label = 'Avg Cadence',
                  method = 'update',
                  args = [{'visible': [False, False, True, False]},
                          {'title': 'Avg Cadence',
                           'showlegend':False}]),
             dict(label = 'Avg Resistance',
                  method = 'update',
                  args = [{'visible': [False, False, False, True]},
                          {'title': 'Avg Resistance',
                           'showlegend':False}]),
            ])
        )
    ],
)
fig.show()

This code produces the following chart:

here

And as you can see, the y-axis toggle works and the scatter points are colored correctly, but I can't seem to get the color legend to show up to show which colors represent which ride duration. I've tried messing around with showLegend, but that only seems to control the dropdown legend for each trace (for output, resistance, and cadence).

I've been able to get a ride duration color legend to display when I use plotly express and hard code one of the workout metrics values to the y-axis, like so:

fig = px.scatter(all_rides_clean, x='created_at', y='Total Output',color="ride.duration")

photo here

but that isn't what I'm looking for because I want the dropdown / ability to dynamically change what's displayed on the y-axis between the different output, resistance, and cadence values.

Anyone know how to get this color legend to display?

Ultimately, I would also like to add another dropdown to be able to dynamically change the discrete data displayed in the color legend (between ride duration, ride type, etc.), so any tips here welcomed.

Thanks in advance!



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source