How to Change Plotly Express bar colors using the Partial Property Update Patch()

I have plot in dash plotly set like this
graph = px.bar(df, x=df[default_x], y=df[default_y], color=df[defualt_color], orientation= "h",)

and change data in patch in the callback as follow

def generate_colors(strings):
    unique_strings = list(set(strings))
    num_unique = len(unique_strings)
    colors = np.linspace(0, 1, num_unique)  # You can choose any colormap you like
    color_dict = {}

    for i, string in enumerate(unique_strings):
        color_dict[string] = colors[i]

    return color_dict

def assign_colors(strings, color_dict):
    colors = []
    for string in strings:
        colors.append(color_dict[string])
    return colors

@app.callback(
    Output('graph', 'figure'),
    Input('x', 'value'),
    Input('y', 'value'),
    Input('color','value')
    )
def graph_change(x, y,color):
    patch_figure = Patch()
    patch_figure ["data"][0]["x"] = df[x]
    patch_figure ["data"][0]["y"] = df[y]
    color_dict = generate_colors(df[color])
    colors = assign_colors(df[color], color_dict)
    patch_figure ['data'][0]['name'] = df[color]
    patch_figure ["data"][0]['line']['color'] = colors
    # patch code to dynamically update figure color input based on

    return patch_figure

but the color and legend are not changing, how can I make it work?

Hey @saraheldawody welcome back to the forums.

Since you are using a bar chart, the syntax should be the following:

patch_figure ["data"][0]['marker']['color'] = colors

I have did the code this way

app.callback(
    Output('graph', 'figure'),
    Input('x', 'value'),
    Input('y', 'value'),
    Input('color','value')
    )
def update_graph(x, y,color):
    patch_figure = Patch()
    color_dict = generate_colors(df[color])
    colors = assign_colors(df[color], color_dict)
    tooltips = [f'{y}: {row[y]}<br>{x}: {row[x]}<br>{color}: {row[color]}<extra></extra>' for index, row in df.iterrows()]
    patch_figure['data'][0]['hovertemplate'] = tooltips
    patch_figure['data'][0]['legendgroup'] = df[color].values
    patch_figure['data'][0]['offsetgroup'] = df[color].values
    patch_figure['data'][0]['name'] = df[color].values
    patch_figure["data"][0]['marker']['color'] = colors
    patch_figure["data"][0]["x"] = df[x].values
    patch_figure["data"][0]["y"] = df[y].values
    patch_figure["layout"]["legend"]["title"]["text"] = color
    patch_figure["layout"]["xaxis"]["title"]["text"] = x
    patch_figure["layout"]["yaxis"]["title"]["text"] = y
    # patch code to dynamically update figure color input based on
    return patch_figure

but the behavior is weired
dirst the legend names and colors are cot changing only the legend title changes
in the bars it self it behaves wiered like in this screenshots
if I chose the same column for color and y it should make the whole bar the same color but it stops in some point and keep the original format for the bar



I see the reason for your confusion. There are a lot of possibilities to change colors. Traces, lines, markers…

To help you further, please provide a MRE which I can copy & paste.

Maybe you are better off changing the default colorscheme:

Here is the code

import json
import numpy as np
import pandas as pd
import plotly.express as px
from dash import Dash, dcc, dash_table, html, Input, Output, State, MATCH, ALL, Patch
import dash_bootstrap_components as dbc
import matplotlib.colors as mcolors
df= pd.read_csv("suicide_rates_1990-2022.csv")
def generate_colors(strings):
    unique_strings = list(set(strings))
    num_unique = len(unique_strings)
    colors = np.linspace(0, 1, num_unique)  # You can choose any colormap you like
    color_dict = {}

    for i, string in enumerate(unique_strings):
        rgba_color = mcolors.hsv_to_rgb([colors[i], 1, 1])
        hex_color = mcolors.to_hex(rgba_color)
        color_dict[string] = hex_color

    return color_dict

def assign_colors(strings, color_dict):
    colors = []
    for string in strings:
        colors.append(color_dict[string])
    return colors

app = Dash(__name__)
df_numeric_data = df.select_dtypes(include=['int', 'float','int64','float64'])
df_string_data = df.select_dtypes(exclude=['int', 'float','int64','float64'])
print(df.info())
unique_numeric_columns = df_numeric_data.columns.unique()
unique_string_columns = df_string_data.columns.unique()


print(unique_numeric_columns[1])
print(unique_string_columns[5])
print(unique_string_columns[3])
my_graph = px.bar(df, x=unique_numeric_columns[1], y=unique_string_columns[5], color=unique_string_columns[3], orientation= "h",)
my_graph.update_layout(margin=dict(l=0, r=0, t=40, b=0, pad=0),
                          title={'xanchor': 'center', 'yanchor': 'top', 'y': 0.99, 'x': 0.5, },

                          plot_bgcolor="white",
                          paper_bgcolor="white",
                          xaxis=dict(showline=True, showgrid=True, showticklabels=True,linecolor="RGBA(224,224,224,1)"),
                          yaxis=dict(showline=True, showgrid=True, showticklabels=True,linecolor="RGBA(224,224,224,1)"))
my_graph.update_yaxes(gridcolor='RGBA(228,225,233,1)', )
fig_json = my_graph.to_json()

with open('fig_json.json', 'w') as f:
    json.dump(json.loads(fig_json), f, indent=4)
app.layout= html.Div([
    html.Div([
                    dbc.FormText("Horizontal (X) Axis"),
                    dcc.Dropdown(unique_numeric_columns, value=unique_numeric_columns[1], id='x'),
                    dbc.FormText("Vertical (Y) Axis"),
                    dcc.Dropdown(unique_string_columns, value=unique_string_columns[5], id='y'),
                    dbc.FormText("Line color: IMPLEMENT PATCH HERE"),
                    dcc.Dropdown(unique_string_columns, value=unique_string_columns[3],id='color',),
                ]),
        html.Div([dcc.Graph(id='graph', figure=my_graph, config={'editable': True})])
])

@app.callback(
    Output('graph', 'figure'),
    Input('x', 'value'),
    Input('y', 'value'),
    Input('color','value')
    )
def graph_update(x, y,color):
    patch_figure = Patch()
    color_dict = generate_colors(df[color])
    colors = assign_colors(df[color], color_dict)
    tooltips = [f'{y}: {row[y]}<br>{x}: {row[x]}<br>{color}: {row[color]}<extra></extra>' for index, row in df.iterrows()]
    patch_figure['data'][0]['hovertemplate'] = tooltips
    patch_figure['data'][0]['legendgroup'] = df[color].values
    patch_figure['data'][0]['offsetgroup'] = df[color].values
    patch_figure['data'][0]['name'] = df[color].values
    patch_figure["data"][0]['marker']['color'] = colors
    patch_figure["data"][0]["x"] = df[x].values
    patch_figure["data"][0]["y"] = df[y].values
    patch_figure["layout"]["legend"]["title"]["text"] = color
    patch_figure["layout"]["xaxis"]["title"]["text"] = x
    patch_figure["layout"]["yaxis"]["title"]["text"] = y
    # patch code to dynamically update figure color input based on
    return patch_figure


app.run_server(debug=True, use_reloader=False)

Here is a dataset that I tested the code with but it behaves strange with the colors, tooltips and the legend names are not changing

I there anyone can help me on this please

Hey @saraheldawody what exactly is it, you need help with?

The issue might be something to do with there being multiple records in the input file for each combination of country and age. Whether or not that’s the problem, it is probably good idea to summarise and filter your data to make the dataframe passed to px.bar as compact as possible for the graph you want to draw.

(Saying that, this dataset doesn’t make a lot of sense to me. It seems to be suicide counts and rates grouped by country, age-group and sex, but there are often multiple records for combinations of those grouping variables, often with different counts and rates. Does it make sense to you?)

When I assign new tooltips they are not replaced it seems they are appended instead which is the code not like this,
also for the legend values it is not changing and the coloring of the bars not colored with new set column, do you know what might be the issue?

I just want to make the x, y, and color columns to be changed by patch when user select it, when I do it without patch like generating the plot from scratch on each change it works, but since patch enhance performance I wanted to use it instead, but the legend is not changing, the colors not set correctly, the tooltips are like some of them not changing, or they are appended not replaced, do you know what maybe the problem is?