Search code examples
pythonplotlybar-chart

How can I add 2 100% stacked bars (Y Axis) to each element in the X Axis of my chart in plotly?


I manged to create the following chart in plotly using python. It's pretty much all I need, but the final thing is to make the whole chart a 100% stacked, so instead o 0.35, the max value in the Y axis would be 1 and all the bars would be evenly aligned. Is there any way to do that in plotly?

Here is the code I used to generate the chart:

fig = go.Figure()
categories = grouped_values2['portfolio_category'].unique()

category_colors = ["#0070C0", "#FFC000", "#AD1457", "#00A651", "#DB4437", "#6E3159", "#F4A000", "#00788C", "#5FB3C3"]

for i, category in enumerate(categories):
  num1 = random.randint(0, 30)
  category_data = grouped_values2[grouped_values['portfolio_category'] == category]
  
  fig.add_trace(
    go.Bar(
      x=category_data['month'],
      y=category_data['importancia_realizado'],
      name=f'R - {category}',
      marker_color=category_colors[i],
      offsetgroup=0
    )
  )
    
  fig.add_trace(
    go.Bar(
      x=category_data['month'],
      y=category_data['importancia_referencia'],
      name=f'P - {category}',
      marker_color=category_colors[i],
      offsetgroup=1
    )
  )

  fig.update_layout(
    barmode='group',
    title='IMPORTÂNCIA PROJETADA (P) vs IMPORTANCIA REALIZADA',
    plot_bgcolor="rgba(0, 0, 0, 0)",
    xaxis_title='Mês',
    yaxis_title='Importância'
  )

fig.show()

enter image description here

Here is the data:

month,month_number,portfolio_category,gmv_mtd,importancia_referencia,importancia_realizado,
0,Apr,4,CAT1,3242989.05,0.230,0.188
1,Apr,4,CAT2,3490670.33,0.190,0.202
2,Apr,4,CAT3,2970437.35,0.170,0.172
3,Apr,4,CAT4,6310704.34,0.160,0.365
4,Apr,4,CAT5,1046692.36,0.120,0.610
5,Apr,4,CAT6,216612.53,0.130,0.130
6,Jun,6,CAT1,4449799.95,0.210,0.274
7,Jun,6,CAT2,5209480.79,0.240,0.321
8,Jun,6,CAT3,2478664.66,0.160,0.153
9,Jun,6,CAT4,1318476.99,0.130,0.810
10,Jun,6,CAT5,2349817.17,0.0,130,0.145
11,Jun,6,CAT6,425692.50,0.130,0.260
12,May,5,CAT1,4057372.58,0.210,0.284
13,May,5,CAT2,3504235.05,0.250,0.245
14,May,5,CAT3,2863510.23,0.170,0.201
15,May,5,CAT4,900040.46,0.130,0.630
16,May,5,CAT5,2564349.59,0.130,0.180
17,May,5,CAT6,385128.35,0.120,0.270

Solution

  • This is tricky but you need to first normalize everything by the totals over each month (basically divide each row by the sum of its month group).

    Then you need to keep track of the base so that we are plotting each bar from the right place. For example, if you want to plot the values [0.1,0.2,0.3,0.4] that add to 1, you want to plot 0.1 with base = 0, then plot 0.2 starting from 0.1, then 0.3 from 0.1+0.2, and 0.4 from 0.1+0.2+0.3.

    from io import StringIO
    
    import pandas as pd
    import plotly.graph_objects as go
    
    sample_data = StringIO("""
    index,month,month_number,portfolio_category,gmv_mtd,importancia_referencia,importancia_realizado,
    0,Apr,4,CAT1,3242989.05,0.230,0.188
    1,Apr,4,CAT2,3490670.33,0.190,0.202
    2,Apr,4,CAT3,2970437.35,0.170,0.172
    3,Apr,4,CAT4,6310704.34,0.160,0.365
    4,Apr,4,CAT5,1046692.36,0.120,0.610
    5,Apr,4,CAT6,216612.53,0.130,0.130
    6,Jun,6,CAT1,4449799.95,0.210,0.274
    7,Jun,6,CAT2,5209480.79,0.240,0.321
    8,Jun,6,CAT3,2478664.66,0.160,0.153
    9,Jun,6,CAT4,1318476.99,0.130,0.810
    10,Jun,6,CAT5,2349817.17,0.0,130,0.145
    11,Jun,6,CAT6,425692.50,0.130,0.260
    12,May,5,CAT1,4057372.58,0.210,0.284
    13,May,5,CAT2,3504235.05,0.250,0.245
    14,May,5,CAT3,2863510.23,0.170,0.201
    15,May,5,CAT4,900040.46,0.130,0.630
    16,May,5,CAT5,2564349.59,0.130,0.180
    17,May,5,CAT6,385128.35,0.120,0.270
    """)
                           
    grouped_values2 = pd.read_csv(sample_data, sep=",")
    grouped_values2 = grouped_values2.assign(importancia_referencia=grouped_values2.importancia_referencia/grouped_values2.groupby('month').importancia_referencia.transform('sum'))
    grouped_values2 = grouped_values2.assign(importancia_realizado=grouped_values2.importancia_realizado/grouped_values2.groupby('month').importancia_realizado.transform('sum'))
    
    fig = go.Figure()
    categories = grouped_values2['portfolio_category'].unique()
    
    category_colors = ["#0070C0", "#FFC000", "#AD1457", "#00A651", "#DB4437", "#6E3159", "#F4A000", "#00788C", "#5FB3C3"]
    
    ## calculate base bars for each month
    months = grouped_values2['month'].unique()
    month_base_values = {
      'importancia_realizado': {k:0 for k in months}, 
      'importancia_referencia': {k:0 for k in months}
    }
    
    for i, category in enumerate(categories):
      # num1 = random.randint(0, 30)
      category_data = grouped_values2[grouped_values2['portfolio_category'] == category]
      
      fig.add_trace(
        go.Bar(
          x=category_data['month'],
          y=category_data['importancia_realizado'],
          base=list(month_base_values['importancia_realizado'].values()),
          name=f'R - {category}',
          marker_color=category_colors[i],
          offsetgroup=0
        )
      )
        
      fig.add_trace(
        go.Bar(
          x=category_data['month'],
          y=category_data['importancia_referencia'],
          base=list(month_base_values['importancia_referencia'].values()),
          name=f'P - {category}',
          marker_color=category_colors[i],
          offsetgroup=1
        )
      )
      
      ## update the base for each group by month
      for k,v in month_base_values['importancia_realizado'].items():
        month_base_values['importancia_realizado'][k] += category_data.loc[category_data['month'] == k,['importancia_realizado']].values[0][0]
        month_base_values['importancia_referencia'][k] += category_data.loc[category_data['month'] == k,['importancia_referencia']].values[0][0]
    
      fig.update_layout(
        barmode='group',
        title='IMPORTÂNCIA PROJETADA (P) vs IMPORTANCIA REALIZADA',
        plot_bgcolor="rgba(0, 0, 0, 0)",
        xaxis_title='Mês',
        yaxis_title='Importância'
      )
    
    fig.show()
    

    enter image description here