Graph Objects not reading colors

Hello,

I’m trying to develop two grouped bar charts sharing an x-axis and legend but showing a different metric. I’m using a column with hex colors in them to set the colors of bars but I’m getting an error. The code is as such:

fig = go.Figure()
    fig =  make_subplots(rows=2, cols=1)

    fig.append_trace(go.Bar(name='MAPE',
                        x= df_table["snapshot"], 
                        y=df_table["MAPE"]),
                        row=1, col=1)
                #  color="model", barmode="group", title = "MAPE by Snapshot")
    fig.append_trace(go.Bar(name='BIAS',
                        x= df_table["snapshot"], 
                        y=df_table["BIAS"]),
                        row=2, col=1)
    fig.update_layout(barmode='group',
    marker_colors=df_final['model_color'],
    labels = df_final['model']
    )

The error is

ValueError: Invalid property specified for object of type plotly.graph_objs.Layout: 'marker'

What am I doing wrong?

The marker is a trace property.

Hence you need to use update_traces, probably in conjunction with the selector parameter or using for_each_trace.

Here an example:

I tried this:

fig = go.Figure()
    fig =  make_subplots(rows=2, cols=1)

    fig.append_trace(go.Bar(name='MAPE',
                        x= df_table["snapshot"], 
                        y=df_table["MAPE"]),
                        row=1, col=1)
                #  color="model", barmode="group", title = "MAPE by Snapshot")
    fig.append_trace(go.Bar(name='BIAS',
                        x= df_table["snapshot"], 
                        y=df_table["BIAS"]),
                        row=2, col=1)
    fig.update_layout(barmode='group')
    fig.update_traces({'marker':{'color':df_final['model_color']}}, selector={'name': df_final['model']})

But I get this:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

I assume you are expecting, that update_traces is looping over all traces and you apply a color to each trace (with the corresponding name as defined by df_final['model']) - which is not the case.

Here an example which corresponds to what you are trying to do:

import plotly.graph_objects as go
import numpy as np

colors = ['red', 'green', 'blue']
names = ['A', 'B', 'C']

def data():
    return np.random.randint(2, 10, 10)

fig = go.Figure()
for name in range(3):
    fig.add_bar(x=np.arange(10), y=data(), name=name, marker_color='orange')
fig.show()

Now we change the marker colors:

it_colors = iter(colors)
fig.for_each_trace(lambda x: x.update(marker_color=next(it_colors)))

I think I may have done something wrong. Here is my callback function:

    colors = ['azure', 'cornflowerblue', 'darkcyan', 'darkgreen', 'darkorange', 'darkslategrey', 'gainsboro']
    names = df_table['model'].unique()
    fig = go.Figure()
    fig = make_subplots(rows=2, cols=1)
    for name in names:
        fig.add_bar(x=df_table[df_table['model']==name]['snapshot'],
                    y = df_table[df_table['model']==name]['MAPE'],
                   name=name,
                    row=1, col=1)
        fig.add_bar(x=df_table[df_table['model']==name]['snapshot'],
                    y = df_table[df_table['model']==name]['BIAS'],
                    name=name,
                    row=2, col=1)
    it_colors = iter(colors)
    fig.for_each_trace(lambda x: x.update(marker_color=next(it_colors)))

But now I’m getting this:

Traceback (most recent call last):
  File "/workspace/test.py", line 311, in time_series_show_snapshot
    fig.for_each_trace(lambda x: x.update(marker_color=next(it_colors)))
  File "/app/.heroku/python/lib/python3.7/site-packages/plotly/graph_objs/_figure.py", line 823, in for_each_trace
    return super(Figure, self).for_each_trace(fn, selector, row, col, secondary_y)
  File "/app/.heroku/python/lib/python3.7/site-packages/plotly/basedatatypes.py", line 1298, in for_each_trace
    fn(trace)
  File "/workspace/test.py", line 311, in <lambda>
    fig.for_each_trace(lambda x: x.update(marker_color=next(it_colors)))
StopIteration

Hi @jbh1128d1 you get this error because the iterator colors is exhausted.

You add len(names) x 2 traces, but len(colors) is only the half of it.

Looking at your callback:

Why don’t you specify the color in the first place when adding the tarces? Like this:

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np

colors = ['red', 'green', 'blue']
names_1 = list('ABC')
names_2 = list('DEF')

def data():
    return np.random.randint(2, 10, 10)

fig = make_subplots(rows=2, cols=1)

for n_1, n_2, color in zip(names_1, names_2, colors):
    fig.add_traces(go.Bar(x=np.arange(10), y=data(), name=n_1, marker_color=color), rows=1, cols=1)
    fig.add_traces(go.Bar(x=np.arange(10), y=data(), name=n_2, marker_color=color), rows=2, cols=1)

fig.show()

1 Like

Thank you. I got it to show up but have double the legend values because both MAPE and BIAS have the same model values. See below:

How can I get only one legend value per model to show that will control both graphs?

I FIGURED IT OUT!!!

I used legendgroups=name.

fig = go.Figure()
    fig = make_subplots(rows=2, cols=1)
    for name, color in zip(names, colors):
        fig.add_traces(go.Bar(x=df_table[df_table['model']==name]['snapshot'],
                    y = df_table[df_table['model']==name]['MAPE'],
                   name=name, marker_color=color,
                   showlegend=True,
                   legendgroup=name),
                   rows=1, cols=1)
        fig.add_traces(go.Bar(x=df_table[df_table['model']==name]['snapshot'],
                    y = df_table[df_table['model']==name]['BIAS'],
                   name=name, marker_color=color,
                   showlegend=False,
                   legendgroup=name),
                    rows=2, cols=1,
                )

Thank you for all the help!