Search code examples
pandasdataframenumpyplotlyplotly-express

How to show multiple text and text position in Python Plotly stacked bar chart


I am displaying the individual value in the stacked bar. I also like to show the total of each stacked bar in the top. Is it possible to do?

My code:

df = pd.DataFrame({'Make':['Mercedes', 'BMW', 'Mercedes', 'Mercedes', 'Chrysler', 'Chrysler', 'Chrysler', 'Chrysler', 'BMW', 'Chrysler', 'BMW', 'Mercedes', 'BMW', 'Mercedes'],
                          'Dimension':['Styling', 'Styling', 'Price', 'Styling', 'MPG', 'MPG', 'Styling', 'Styling', 'MPG', 'MPG', 'Price', 'Price', 'Styling', 'MPG'],
                          'Country':['USA', 'USA', 'USA', 'Germany', 'USA', 'USA', 'USA', 'England', 'Germany', 'USA', 'Germany', 'Poland', 'Italy', 'USA'],
                          'LowValue':['64', '61', '70', '65', '59', '68', '63', '57', '58', '55', '69', '63', '69', '61']})
df_make = df.groupby(['Make','Dimension']).count()[['LowValue']].reset_index()

fig = px.bar(
    data_frame=df_make,
    x='Make',
    y="LowValue",
    color="LowValue",
    text="LowValue",
    
)
fig.update_traces(textposition='inside')

fig.show()

enter image description here


Solution

  • To my knowledge there's no way to do this directly. But you could always use fig.add_annotation() with a setup like this:

    for i,t in enumerate(totals):
        fig.add_annotation(x=x_uniq[i], y = t,
                           text = str(t),
                           showarrow = False,
                           yshift = 12,
                           font=dict(family="Courier New, monospace",
                                     size=18,
                                     color="firebrick"
                                    )
                          )
    

    Where x_uniq are the unique occurences ['BMW', 'Chrysler', 'Mercedes'] of the figure's x-values ['BMW','BMW', 'BMW', 'Chrysler', 'Chrysler', 'Mercedes', 'Mercedes', 'Mercedes'] and totals are the corresponding partial sums of the figure's y-values [1, 1, 2, 3, 2, 1, 2, 2] mapped to the x-values. The complete snippet below contains all details on how to get those values and produce this figure:

    enter image description here

    Complete code:

    import plotly.graph_objects as go
    import plotly.express as px
    import pandas as pd
    import numpy as np
    from itertools import groupby
    
    df = pd.DataFrame({'Make':['Mercedes', 'BMW', 'Mercedes', 'Mercedes', 'Chrysler', 'Chrysler', 'Chrysler', 'Chrysler', 'BMW', 'Chrysler', 'BMW', 'Mercedes', 'BMW', 'Mercedes'],
                              'Dimension':['Styling', 'Styling', 'Price', 'Styling', 'MPG', 'MPG', 'Styling', 'Styling', 'MPG', 'MPG', 'Price', 'Price', 'Styling', 'MPG'],
                              'Country':['USA', 'USA', 'USA', 'Germany', 'USA', 'USA', 'USA', 'England', 'Germany', 'USA', 'Germany', 'Poland', 'Italy', 'USA'],
                              'LowValue':['64', '61', '70', '65', '59', '68', '63', '57', '58', '55', '69', '63', '69', '61']})
    df_make = df.groupby(['Make','Dimension']).count()[['LowValue']].reset_index()
    
    fig = px.bar(
        data_frame=df_make,
        x='Make',
        y="LowValue",
        color="LowValue",
        text="LowValue",
        
    )
    fig.update_traces(textposition='inside')
    
    L = list(fig.data[0].x)
    x_uniq, x_counts = np.unique(L, return_counts=True)
    y_vals = fig.data[0].y
    y_vals
    
    
    elements = list(x_counts)
    
    totals = []
    index = 0
    for i, e in enumerate(elements):
        if i == 0:
            totals.append(sum(y_vals[0:e]))
            index = index + e
        else:
            totals.append(sum(y_vals[index:index + e]))
            index = index + e
    totals
    
    for i,t in enumerate(totals):
        fig.add_annotation(x=x_uniq[i], y = t,
                           text = str(t),
                           showarrow = False,
                           yshift = 12,
                           font=dict(family="Courier New, monospace",
                                     size=18,
                                     color="firebrick"
                                    )
                          )
    fig.show()