What is the most efficient way to use the color of a discrete sequence by legendgroup, rather than by trace?

Hello,

Say we start with something like that :

import plotly.graph_objects as go
import plotly.express as px
import pandas as pd

df = px.data.gapminder().query("continent == 'Oceania'")
fig = go.Figure()
for iso in df["iso_alpha"].unique()[0:5]:
    df_temp = df[df["iso_alpha"] == iso]
    fig.add_trace(go.Scatter(
        x=df_temp["year"],
        y=df_temp["gdpPercap"],
        name="gdpPerCap", 
        legendgroup=iso,
        legendgrouptitle_text=iso,
    ))
    fig.add_trace(go.Scatter(
        x=df_temp["year"],
        y=[df_temp["gdpPercap"].mean()] * len(df_temp["year"]),
        name="reference",
        legendgroup = iso,
        legendgrouptitle_text = iso,
    ))


The rendered figure would be:

I would like to see the “reference” line of group AUS in blue (2nd trace of the first group),
the gdpPercap of NZL in red, and the “reference” of NZL in red too.
The “greenish” color should be used for the third group of traces, the purple one for the fourth group, and so forth and so on

In other world, I would like that the color sequence is iterated over the legend group, and not over the traces.

By default, if the color sequence is too short, dash/plotly iterates over again and again, which is okish in my case. And, given this routine, I don’t need to specify or map a specific color at each loop iteration.

Given the output I’ld like to get, and given that I use a custom color sequence (12 colors, predefined template) as colorway (actually, this does not change the problem)

pio.templates["corporate_identity"] = go.layout.Template(
    layout=dict(
        colorway=list(colorPalette["Sequence_4"].values()),
        # colorPalette["Sequence_4"] is a dict structured as {named color One: rgba(), ...}
    )
)

and since I don’t wish to write the name of each color, it seems that I must:

  1. build an array of colors as long as the length of the list of unique iso_alpha codes
    Q: if I have 17 iso_alpha code, is it better to make a colorway of 12+5 repeated colors, exactly, or to append the full color sequence one more time to the original color sequence to get an array of 24 element ? Just to keep it clean…
  2. then, for each iso_code, iterate over the array of colorcode in the colorway.

It’s not a big deal, but is there a feature that I might have missed to do that already out of the box?

By the way, it could be nice if dash could automatically change the symbol for the traces, inside each group.

Found a solution that looks clean by using the cycle function from itertools:

a = ["A", "B", "C"]
b = ["L", "M", "N", "O"]
c = ["A", "B", "C", "D", "E", "F"]
for a, b in zip(a, cycle(b)):
    print(a,b)      
# Output:
# A L
# B M
# C N

for b,a in zip(b, cycle(a)):
    print(b,a)      
# Output:
# L A
# M B
# N C
# O A

So:

import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
from itertools import cycle
colorlist= ["red", "blue", "green"]

df = px.data.gapminder().query("continent == 'Oceania'")
fig = go.Figure()
for iso, c in zip(df["iso_alpha"].unique()[0:5], cycle(colorlist)):
    df_temp = df[df["iso_alpha"] == iso]
    fig.add_trace(go.Scatter(
        x=df_temp["year"],
        y=df_temp["gdpPercap"],
        name="gdpPerCap", 
        line=dict(color=c),
        legendgroup=iso,
        legendgrouptitle_text=iso,
    ))
    fig.add_trace(go.Scatter(
        x=df_temp["year"],
        y=[df_temp["gdpPercap"].mean()] * len(df_temp["year"]),
        name="reference",
        line=dict(color=c),
        legendgroup = iso,
        legendgrouptitle_text = iso,
    ))

image

That simple…