Apply "color groups" for multiple traces

I have a figure with 3 line scatters, and for each of them I want to mark a specific point. I’m doing so by adding for each another scatter with a single point in the x, y I want.
I would like for the scatter and the single-point scatter to have the same color, which I can define explicitly but I also want to be able to not decide on the colors in the creation of the figure and be able to later control it using themes/templates.

So I’m looking for a solution to put two scatter traces in the same “color group” without defining the color explicitly.

import numpy as np
import plotly.graph_objects as go

size = 10

x = list(range(size))

y = {
    'a': np.random.rand(size),
    'b': np.random.rand(size),
    'c': np.random.rand(size)
}

index_to_higlight = {
    'a': 2,
    'b': 5,
    'c': 1
}

fig = go.Figure()
for plot in ['a', 'b', 'c']:
    # Both scatters should have the same color! 
    # but colors should be editable later using templates
    fig.add_trace(go.Scatter(x=x, y=y[plot], name=plot))
    fig.add_trace(go.Scatter(x=[x[index_to_higlight[plot]]], 
                             y=[y[plot][index_to_higlight[plot]]], 
                             mode='markers', 
                             marker_size=25, 
                             showlegend=False))

Thanks.

Hi @matanper! I know it’s been a long time since you posted but I found a solution and I wanted to share it here in case somebody arrives here trying to solve the same problem.

With this code the colors for the markers and the lines would be the same and they would change if the template does:

fig = go.Figure()
for i, plot in enumerate(['a', 'b', 'c']):
    fig.add_trace(go.Scatter(x=x, y=y[plot],  
                             marker_color = fig.layout['template']['layout']['colorway'][i]))
    fig.add_trace(go.Scatter(x=[x[index_to_higlight[plot]]], 
                             y=[y[plot][index_to_higlight[plot]]], 
                             mode='markers', 
                             marker_size=25,
                             marker_color=fig.layout['template']['layout']['colorway'][i],
                             showlegend=False))

Another option is to give both traces (line + point to highlight) the same name and apply later a conditional update_traces:

fig = go.Figure()
for i, plot in enumerate(['a', 'b', 'c']):
    fig.add_trace(go.Scatter(x=x, y=y[plot], 
                             name = plot, 
                             mode = 'lines+markers')
    fig.add_trace(go.Scatter(x=[x[index_to_higlight[plot]]], 
                             y=[y[plot][index_to_higlight[plot]]], 
                             name = plot,
                             mode='markers', 
                             marker_size=25,
                             showlegend=False))
    fig.update_traces(
        marker=dict(color=fig.layout['template']['layout']['colorway'][i]), 
        selector=dict(name=plot))
2 Likes

Thanks! better late than never :smiley:

It’s better than what I had so far, but the solution is still only halfway for me. My use-case is creating the plots in one system, and then saving them and loading them in other systems (which might have different themes), but looks like in your solution the plots will be saved with the theme that was active at creation time.

The best solution for me would be to have a “marker color index” which accepts index instead of direct color.

Hmm how do you exactly save them? The other option I can think of is this one:

marker_color = pio.templates[pio.templates.default]['layout']['colorway'][i]

But I guess it would have the same problem (it uses the active template in the first system). Although you could use it in combination with the second solution from the previous post. When you ‘load’ the figures in the second system, you can apply:

import plotly.io as pio

for i, plot in enumerate(['a', 'b', 'c']):
    fig.update_traces(
        marker_color = pio.templates[pio.templates.default]['layout']['colorway'][i],
        selector=dict(name=plot))

If you want a more automated system (not having to remember the traces names):

import plotly.io as pio

traces = list(set(
    [fig.data[trace].name for trace in range(len(fig.data)) if fig.data[trace].name is not None]
))

for i, plot in enumerate(traces):
    fig.update_traces(
        marker_color = pio.templates[pio.templates.default]['layout']['colorway'][i],
        selector=dict(name=plot))

Thanks! I’ll try that out

1 Like

@matanper , It’s also possible to pass an array into the marker_size attribute so you can do all of this in a single trace:

import numpy as np
import plotly.graph_objects as go

size = 10

x = list(range(size))

y = {
    'a': np.random.rand(size),
    'b': np.random.rand(size),
    'c': np.random.rand(size)
}

index_to_higlight = {
    'a': 2,
    'b': 5,
    'c': 1
}

fig = go.Figure()
for plot in ['a', 'b', 'c']:
    fig.add_trace(
        go.Scatter(
            x=x, y=y[plot], name=plot, mode='lines+markers',
            marker_size=[25 if i == index_to_higlight[plot] else 8 for i in range(size)])
    )

Thanks! is it possible to also add hover_data only on the index_to_highlight?