hvahva
hvahva

Reputation: 51

How to order stacked bar chart in python?

I have a data frame:

import pandas as pd
data = {
'BU': ['A', 'C', 'C', 'E', 'E', 'A', 'D', 'C', 'D', 'E', 'A', 'A', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'E', 'E', 'A', 'A'],
'Sub - BU': ['A2', 'B1', 'C1', 'D1', 'E1', 'A1', 'B1', 'C1', 'D1', 'E2', 'A1', 'B1', 'C1', 'D2', 'E2', 'A1', 'B1', 'C1', 'D1', 'E1', 'C1', 'D2', 'E1', 'E2', 'A1', 'A1'],
'Initial contract result': [0, 2, 0, 1, 3, 2, 0, 0, 0, 9, 0, 5, 2, 0, 0, 8, 7, 4, 0, 0, 0, 0, 5, 0, 8, 4]} 
df_cur = pd.DataFrame(data)

df_cur['Contract Sum Type'] = df_cur['Initial contract result'].apply(lambda x: 'Zero' if x == 0 else 'Non-zero')
counts_df = df_cur.groupby(['BU', 'Sub - BU', 'Contract Sum Type']).size().reset_index(name='Count')

group_totals = counts_df.groupby(['BU', 'Sub - BU'])['Count'].transform('sum')
counts_df['Percentage'] = 100 * counts_df['Count'] / group_totals

counts_df['Total Count'] = counts_df.groupby('Sub - BU')['Count'].transform('sum')

Now I want to plot this as a stacked bar chart. On the x axis I want to see the Sub - BU but also I want it to be grouped by the BU column and each BU should get its own colors. Also the stacked bar chart should be descending. How to do this, because the fig.updatelayout commant doesnt seem to do anything in this also I tried creating a subset where I filter the dataframe descending based on the Total Count column but this didnt work.

import plotly.express as px
fig = px.bar(counts_df, x='Sub - BU', y='Count', color='Contract Sum Type', 
         title='Initial Contract Sum by Sub - BU', barmode='stack', text_auto=True)

fig.update_layout(yaxis={'categoryorder':'total ascending'})

fig.show()

Upvotes: 2

Views: 199

Answers (2)

r-beginners
r-beginners

Reputation: 35155

To create a grouping and stacking graph, the x-axis can be graphed by specifying a grouping column and a subgroup column in the list.

import plotly.graph_objects as go

fig = go.Figure()

for t in counts_df['Contract Sum Type'].unique():
    dff = counts_df[counts_df['Contract Sum Type'] == t]
    fig.add_trace(go.Bar(x=[dff['BU'], dff['Sub - BU']], y=dff['Count'], name=t))

fig.update_layout(barmode='stack', legend_title='Contract Sum Type')
fig.show()

enter image description here

Set different colours for different groups


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

colors = px.colors.qualitative.T10

fig = go.Figure()
i = 0
for t in counts_df['Contract Sum Type'].unique():
    for g in counts_df['BU'].unique():
        dff = counts_df[(counts_df['Contract Sum Type'] == t) & (counts_df['BU'] == g)]
        fig.add_trace(go.Bar(x=[dff['BU'], dff['Sub - BU']], y=dff['Count'], name=t, marker=dict(color=colors[i])))
        i += 1

# 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(barmode='stack', legend_title='Contract Sum Type')
fig.show()

enter image description here

Upvotes: 2

Unless I misunderstand your question you could do something like this:

import pandas as pd
import plotly.express as px

data = {
    'BU': ['A', 'C', 'C', 'E', 'E', 'A', 'D', 'C', 'D', 'E', 'A', 'A', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'E', 'E', 'A', 'A'],
    'Sub - BU': ['A2', 'B1', 'C1', 'D1', 'E1', 'A1', 'B1', 'C1', 'D1', 'E2', 'A1', 'B1', 'C1', 'D2', 'E2', 'A1', 'B1', 'C1', 'D1', 'E1', 'C1', 'D2', 'E1', 'E2', 'A1', 'A1'],
    'Initial contract result': [0, 2, 0, 1, 3, 2, 0, 0, 0, 9, 0, 5, 2, 0, 0, 8, 7, 4, 0, 0, 0, 0, 5, 0, 8, 4]
}

df_cur = pd.DataFrame(data)

df_cur['Contract Sum Type'] = df_cur['Initial contract result'].apply(lambda x: 'Zero' if x == 0 else 'Non-zero')
counts_df = df_cur.groupby(['BU', 'Sub - BU', 'Contract Sum Type']).size().reset_index(name='Count')

group_totals = counts_df.groupby(['BU', 'Sub - BU'])['Count'].transform('sum')
counts_df['Percentage'] = 100 * counts_df['Count'] / group_totals

counts_df['Total Count'] = counts_df.groupby('Sub - BU')['Count'].transform('sum')

# sort by descending Total Count within each BU
counts_df = counts_df.sort_values(['BU', 'Total Count'], ascending=[True, False])

fig = px.bar(counts_df, x='Sub - BU', y='Count', color='Contract Sum Type',
             title='Initial Contract Sum by Sub - BU', barmode='stack',
             text='Percentage', facet_col='BU', color_discrete_sequence=px.colors.qualitative.Set1)

fig.update_layout(yaxis={'categoryorder': 'total descending'})

fig.show()

which give

enter image description here

Upvotes: 2

Related Questions