Python Plotly - 堆叠 + 分组柱形图

9

我正在尝试使用Python中的Plotly创建一个既可以分组又可以堆叠的条形图。
玩具示例(不同年份花费和收入):

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()   

起初看起来结果还不错: enter image description here 但是当我关闭“花在水果上”的选项时,请看接下来发生了什么: enter image description here “花在玩具上”的轨迹仍然浮动,而不是从0开始。
这能修复吗?或者也许整个offsetgroup + base方法在这里行不通。但是我能做什么其他的呢?
谢谢!

更新:根据这个 Github 问题,堆积、分组条形图正在开发中,所以这可能不再是一个问题。


你为什么要堆叠 var1 和 var2? - Jussi Nurminen
在我的真实数据中,这是有意义的,因为var1和var2的总和具有某种含义(var3和var4也是如此)。我稍微修改了一下示例,使其也有些意义。 - soungalo
我明白了。显然,当你关闭跟踪时,base 没有被修改。我的 Plotly 知识在这里不够深入,但如果 Plotly 支持某种回调来切换曲线的开关,那么可以根据需要修改 base - Jussi Nurminen
您的图表为我省去了很多烦恼。非常感谢您,先生! - borisdonchev
2个回答

11

Plotly Express(最近的plotly库版本的一部分)为其条形图(以及其他图表)提供了一个facet_col参数,允许您设置一个附加分组列:

来自此列或类似于数组的值用于在水平方向上将标记分配给分面子图。

为了使其工作,我不得不重新塑造示例数据:

import pandas as pd

data = pd.DataFrame(
    dict(
        year=[*[2000, 2010, 2020]*4],
        var=[*[10, 20, 15], *[12, 8, 18], *[10, 17, 13], *[12, 11, 20]],
        names=[
            *["spent on fruit"]*3,
            *["spent on toys"]*3,
            *["earned from stocks"]*3,
            *["earned from gambling"]*3,
        ],
        groups=[*["subgroup1"]*6, *["subgroup2"]*6]
    )
)
年份 数值 项目名称 子组名称
0 2000 10 水果支出 子组1
1 2010 20 水果支出 子组1
2 2020 15 水果支出 子组1
3 2000 12 玩具支出 子组1
4 2010 8 玩具支出 子组1
5 2020 18 玩具支出 子组1
6 2000 10 股票收益 子组2
7 2010 17 股票收益 子组2
8 2020 13 股票收益 子组2
9 2000 12 赌博收益 子组2
10 2010 11 赌博收益 子组2
11 2020 20 赌博收益 子组2

转换成这种格式后(我相信这被称为"长格式"),您可以通过一次函数调用绘制它:

import plotly_express as px

fig = px.bar(data, x="groups", y="var", facet_col="year", color="names")
fig.show()

若想隐藏子组标签,可更新x轴:

Plotly express bar chart grouped and stacked

fig.update_xaxes(visible=False)

Plotly express bar chart grouped and stacked without x-axis labels


如果我只想添加子组的标签而不包括组名,你知道该怎么做吗? - Hamzah
1
这个回答中的第一点有帮助吗? - Saaru Lindestøkke
非常感谢,我按照提供的链接解决了问题。但是我卡在了如何删除每个组上方的标签,“年份”在你的例子中。或者至少将它们下移而不是放在图形上方。 - Hamzah
非常抱歉要重复一遍,但你试过这个答案中第1点建议了吗?它是代码段 1. 隐藏分组标题 中的代码片段。如果你运行那段代码,它会移除每个组上方的标签。如果这对你不起作用,最好是提出一个新问题,并说明你到目前为止尝试了什么以及卡在哪里。 - Saaru Lindestøkke
非常感谢,这解决了我的问题 :) - Hamzah
显示剩余3条评论

8

似乎没有办法在Plotly中创建既有堆叠,又有分组的条形图,但是有一个解决方法可能会解决您的问题。您需要创建子组,然后在Plotly中使用堆叠条形图,一次绘制一个条形图,用子组1绘制var1var2,用子组2绘制var3var4

这种解决方案提供了您想要的功能,但更改了条形图的格式和美观度。每个条之间将具有相等的间距,因为从Plotly的角度来看,这些都是堆叠在一起的条形图(而不是分组的条形图),我无法找到去除子组1和子组2文本的方法,同时保留x轴刻度中的年份。任何Plotly专家请随时加入并改善我的答案!

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


2
谢谢,这很聪明!但这也相当繁琐,也许需要向Plotly团队提出功能请求。 - soungalo
另外,如果我不想显示“subgroup1”/“subgroup2”的标签怎么办?如何将年份标签旋转45度或90度? - soungalo
我会看看能否弄清楚 subgroup1 / subgroup2 标签的问题。我无法确定是否有哪个参数可以修改以删除这些子组名称而不删除年份。我尝试了一种方法,即将空字符串 ''None 作为子组的名称传递,但这些会使分组的条形图崩溃。 - Derek O

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接