How to share a common color scale among different bar traces?

I’m using plotly Python to make a graph showing my day. Here’s an example:

and here’s the code that does this:

timeentry = namedtuple("timeentry", ["delta", "category", "activity"])
def make_stacked_bar(entries: list[timeentry]):
    import plotly.graph_objects as go
    import plotly

    secs_per_minute = 60
    time_label = "minutes"
    times = [d.delta.total_seconds()/secs_per_minute for d in entries]
    categories = [d.category for d in entries]

    color_map = {}
    for category in set(categories):
       color_map[category] = len(color_map)

    colorscale = 'viridis'
    # colors = plotly.colors.qualitative.dark24

    fig = go.figure()
    for idx, category in enumerate(categories):
        # print(f"category: {category} with color = {color_map[category]}")
        fig.add_trace(go.bar(
            x=[time_label],
            y=[times[idx]],
            name=category,
            text=[category],
            textposition='auto',
            marker=dict(colorscale=colorscale, cmin=0, cmax=len(color_map) - 1, color=color_map[category]),
            # marker=dict(color=colors[color_map[category]]),
            width=0.7,
            showlegend=false
        ))

    fig.update_layout(yaxis_title=time_label,height=1000, barmode='stack')
    fig.show()

Note, that there’s a bug in my chart. Specifically, 2 different bars with the same name, like “Work” might have different colors. I can fix this bug by explicitly setting a color using something like marker=dict(color=plotly.colors.qualitative.dark24[color_map[category]], but this looks much less aesthetically pleasing than using the Viridis color scale.

Does anyone know how I can fix my code such that it uses the viridis color scale?

Hi @vedantroy welcome to the forums.

I did not fully understand what you want to do. Instead of using dark24 you want to use viridis? The question is, how do you want to use it, here are three options (I added dark24 for clarity reasons):

newplot(37)

code:

from plotly.express.colors import sample_colorscale
from sklearn.preprocessing import minmax_scale

import plotly.graph_objects as go
import itertools as it
import numpy as np

# create data
x = np.ones(24)
y = np.arange(0,24)

cycled = it.cycle(plotly.colors.sequential.Viridis)
cycled_viridis24 = [next(cycled) for _ in y]
discrete_viridis24 = sample_colorscale('Viridis', minmax_scale(y))
# ^^ you could do the min/max scaling wthout sklearn:
# y_scaled = [(val - y.min()) / (y.max() - y.min()) for val in y]
# discrete_viridis24 = sample_colorscale('Viridis', y_scaled)

fig = go.Figure()
fig.add_scatter(x=x, y=y, mode='markers', marker={'size':10, 'size':10, 'color': y, 'colorscale': 'Viridis'}, name='sequential viridis')
fig.add_scatter(x=x*2, y=y, mode='markers', marker={'size':10, 'color': plotly.colors.qualitative.Dark24}, name='qualitative dark24')
fig.add_scatter(x=x*3, y=y, mode='markers', marker={'size':10, 'color': cycled_viridis24}, name='cycled viridis')
fig.add_scatter(x=x*4, y=y, mode='markers', marker={'size':10, 'color': discrete_viridis24}, name='discrete viridis')

fig.update_layout(width=600, height=500)

This is also a really interesting if you want to create a discrete colorscale from a continous colorscale:

1 Like

@AIMPED

Thanks, the continuous => discrete colorscale trick was useful.