1
votes

I'm trying to create a bar chart using plotly in python, which is both stacked and grouped.
Toy example (money spent and earned in different years):

import pandas as pd
import plotly.graph_objs as go

data = pd.DataFrame(
    dict(
        year=[2000,2010,2020],
        var1=[10,20,15],
        var2=[12,8,18],
        var3=[10,17,13],
        var4=[12,11,20],
    )
)

fig = go.Figure(
    data = [
        go.Bar(x=data['year'], y=data['var1'], offsetgroup=0, name='spent on fruit'),
        go.Bar(x=data['year'], y=data['var2'], offsetgroup=0, base=data['var1'], name='spent on toys'),
        go.Bar(x=data['year'], y=data['var3'], offsetgroup=1, name='earned from stocks'),
        go.Bar(x=data['year'], y=data['var4'], offsetgroup=1, base=data['var3'], name='earned from gambling'),
    ]
)
fig.show()   

The result seems fine at first: enter image description here But watch what happens when I turn off e.g. "spent on fruit": enter image description here The "spent on toys" trace remains floating instead of starting from 0.
Can this be fixed? or maybe the whole offsetgroup + base approach won't work here. But what else can I do?
Thanks!

Update: according to this Github issue, stacked, grouped bar plots are being developed for future plotly versions, so this probably won't be an issue anymore.

1
Why do you stack var1 and var2?Jussi Nurminen
Well, in my real data this makes sense b/c the total of var1 and var2 has a certain meaning (and so do var3 and var4). I've modified the example a bit so it makes some sense too.soungalo
I see. Obviously base is not modified when you switch off the trace. My plotly knowledge is not deep enough here, but if plotly supports some kind of callback on switching curves on and off, that might be used to modify base on demand.Jussi Nurminen

1 Answers

3
votes

There doesn't seem to be a way to create both stacked and grouped bar charts in Plotly, but there is a workaround that might resolve your issue. You will need to create subgroups, then use a stacked bar in Plotly to plot the bars one at a time, plotting var1 and var2 with subgroup1, and var3 and var4 with subgroup2.

This solution gives you the functionality you want, but changes the formatting and aesthetic of the bar chart. There will be equal spacing between each bar as from Plotly's point of view these are stacked bars (and not grouped bars), and I couldn't figure out a way to eliminate the subgroup1 and subgroup2 text without also getting rid of the years in the x-axis ticks. Any Plotly experts please feel free to chime in and improve my answer!

import pandas as pd
import plotly.graph_objs as go

df = pd.DataFrame(
    dict(
        year=[2000,2010,2020],
        var1=[10,20,15],
        var2=[12,8,18],
        var3=[10,17,13],
        var4=[12,11,20],
    )
)
        
fig = go.Figure()

fig.update_layout(
    template="simple_white",
    xaxis=dict(title_text="Year"),
    yaxis=dict(title_text="Count"),
    barmode="stack",
)

groups = ['var1','var2','var3','var4']
colors = ["blue","red","green","purple"]
names = ['spent on fruit','spent on toys','earned from stocks','earned from gambling']

i = 0
for r, n, c in zip(groups, names, colors):
    ## put var1 and var2 together on the first subgrouped bar
    if i <= 1:
        fig.add_trace(
            go.Bar(x=[df.year, ['subgroup1']*len(df.year)], y=df[r], name=n, marker_color=c),
        )
    ## put var3 and var4 together on the first subgrouped bar
    else:
        fig.add_trace(
            go.Bar(x=[df.year, ['subgroup2']*len(df.year)], y=df[r], name=n, marker_color=c),
        )
    i+=1

fig.show()   

enter image description here