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:
But watch what happens when I turn off e.g. "spent on fruit":
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.
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()