Stack bar chart color sorting

Hi all,

I’m trying to build a stacked bar chart and I want each bar to have its own sorting.

My X-axis is months of a year, my Y-axis is the percentage. Each month has several categories (which are different languages) with different percentages. In one month, Russian might be higher and in another, English. So I need to sort the languages based on that specific month.

Here is a simplified version of the code:


import plotly.express as px


total = total.sort_values(['Month', 'country', 'percentage']).reset_index(drop=True)

# Create the facet grid bar chart
fig = px.bar(total, x='Month', y='percentage_comp', color='Grouped Language',
             facet_col='country', facet_col_wrap=2,
             title='', text='percentage', labels={'percentage': 'Percentage'},
             color_discrete_sequence=px.colors.qualitative.T10)

# Show the plot
fig.show()

Here is what I have right now:

I’d very much appreciate any comment.

hi @Mitra_Mir
:wave: Welcome to the community.

Can you please share the data with us as well, so we can run your code on our computer. That would make it easier to help. You can share the data, for example, via github or google drive.

1 Like

Hi @adamschroeder,

Here is a colab notebook with the data and visualization: Google Colab

Where can I find this csv file?

total=pd.read_csv('Countries_langdetected.csv')

Thanks for pointing it out. It’s the same as the dataset I build in the notebook. If you run all the cells you should be able to see the plots.

Since sorting is not possible in plotly.express," we first create a data frame extracted from the monthly list by the target month using a graph object. Next, create a data frame by country from the list of countries. Then sort that data frame by percentage. Loop through the sorted data frames, starting from the first row to the last row. This method is just like piling up building blocks. If you set up a legend group, clicking on the legend hides the language. The number of legends can be as large as the number of loop operations, so they are uniquely grouped together.

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

colors = {'fr':'blue', 'en': 'red', 'other': 'green'}

fig = make_subplots(rows=2, cols=2,
                    subplot_titles=("France","Australia","US"),
                    vertical_spacing=0.1,
                    shared_xaxes=True,
                    )
#fig = go.Figure()

for m in sorted(df['Month'].unique()):
    dfm = df.query('Month ==@m')
    for c,rc in zip(['France','Australia','US'], [(1,1),(1,2),(2,1)]):
        dfc = dfm.query('country == @c')
        dfc = dfc.sort_values('percentage_comp', ascending=False)
        dfc.reset_index(drop=True, inplace=True)
        # print(dfc)
        for i in range(len(dfc)):
            fig.add_trace(go.Bar(
                x=[dfc.loc[i,'Month']],
                y=[dfc.loc[i,'percentage_comp']],
                text='{:.0f}'.format(dfc.loc[i,"percentage_comp"]),
                textposition='auto',
                name=dfc.loc[i,'Grouped Language'],
                marker=dict(color=colors[dfc.loc[i,'Grouped Language']]),
                legendgroup=dfc.loc[i,'Grouped Language'],
                showlegend=True,
            ), row=rc[0], col=rc[1])
        fig.update_layout(barmode='stack')

names = set()
fig.for_each_trace(
    lambda trace:
        trace.update(showlegend=False)
        if (trace.name in names) else names.add(trace.name))

fig.update_layout(height=1000,legend_title_text='Legend')
fig.show()

1 Like