简体   繁体   English

python / plotly(快递)中的堆叠条形图 plot:条形图的分组/排序

[英]Stacked bar plot in python / plotly (express): grouping / ordering of bars

I have data in a dataframe that I want to plot with a stacked bar plot:我在 dataframe 中有数据,我想使用堆叠条 plot plot:

test_df = pd.DataFrame([[1, 5, 1, 'A'], [2, 10, 1, 'B'], [3, 3, 1, 'A']], columns = ('ID', 'Value', 'Bucket', 'Type'))

if I do the plot with Plotly Express I get bars stacked on each other and correctly ordered (based on the index):如果我将 plot 与 Plotly Express 一起使用,我会得到彼此堆叠并正确排序的条形(基于索引):

fig = px.bar(test_df, x='Bucket', y='Value', barmode='stack')

However, I want to color the data based on Type, hence I go for但是,我想根据类型为数据着色,因此我为 go

fig = px.bar(test_df, x='Bucket', y='Value', barmode='stack', color='Type')

在此处输入图像描述

This works, except now the ordering is messed up, because all bars are now grouped by Type.这行得通,但现在排序混乱了,因为现在所有条形图都按类型分组。 I looked through the docs of Plotly Express and couldn't find a way to specify the ordering of the bars independently.我查看了 Plotly Express 的文档,找不到单独指定条形排序的方法。 Any tips on how to do this?关于如何做到这一点的任何提示?

I found this one here, but the scenario is a bit different and the options mentioned there don't seem to help me: How to disable plotly express from grouping bars based on color?我在这里找到了这个,但情况有点不同,那里提到的选项似乎对我没有帮助: How to disable plotly express from grouping bars based on color?

Edit: This goes into the right direction, but not with using Plotly Express, but rather Plotly graph_objects:编辑:这进入了正确的方向,但不是使用 Plotly Express,而是使用 Plotly graph_objects:

import plotly.graph_objects as go
test_df = pd.DataFrame([[1, 5, 1, 'A', 'red'], [2, 10, 1, 'B', 'blue'], [3, 3, 1, 'A', 'red']], columns = ('ID', 'Value', 'Bucket', 'Type', 'Color'))
fig = go.Figure()
fig.add_trace(go.Bar(x=test_df["Bucket"], y=test_df["Value"], marker_color=test_df["Color"]))

Output: Output: 在此处输入图像描述

Still, I'd prefer the Express version, because so many things are easier to handle there (Legend, Hover properties etc.).不过,我更喜欢 Express 版本,因为那里有很多东西更容易处理(Legend、Hover 属性等)。

The only way I can understand your question is that you don't want B to be stacked on top of A , but rather the opposite.我能理解您的问题的唯一方法是您不希望B堆叠在A之上,而是相反。 If that's the case, then you can get what you want through:如果是这种情况,那么您可以通过以下方式获得您想要的东西:

fig.data = fig.data[::-1]
fig.layout.legend.traceorder = 'reversed'

在此处输入图像描述

Some details:一些细节:

fig.data = fig.data[::-1] simply reverses the order that the traces appear in fig.data and ultimately in the plotted figure itself. fig.data = fig.data[::-1]只是颠倒了痕迹出现在fig.data中的顺序,最终出现在绘制的图形本身中。 This will however reverse the order of the legend as well.然而,这也会颠倒图例的顺序。 So without fig.layout.legend.traceorder = 'reversed' the result would be:因此,如果没有fig.layout.legend.traceorder = 'reversed' ,结果将是:

在此处输入图像描述

And so it follows that the complete work-around looks like this:因此,完整的解决方法如下所示:

fig.data = fig.data[::-1]
fig.layout.legend.traceorder = 'reversed'

Complete code:完整代码:

import pandas as px
import plotly.express as px
test_df = pd.DataFrame([[1, 5, 1, 'A'], [2, 10, 1, 'B'], [3, 3, 1, 'A']], columns = ('ID', 'Value', 'Bucket', 'Type'))
fig = px.bar(test_df, x='Bucket', y='Value', barmode='stack', color='Type')
fig.data = fig.data[::-1]
fig.layout.legend.traceorder = 'reversed'
fig.show()

Ok, sorry for the long delay on this, but I finally got around to solving this.好的,很抱歉耽搁了这么久,但我终于开始解决这个问题了。
My solution is possibly not the most straight forward one, but it does work.我的解决方案可能不是最直接的解决方案,但它确实有效。

The basic idea is to use graph_objects instead of express and then iterate over the dataframe and add each bar as a separate trace.基本思想是使用 graph_objects 而不是 express,然后遍历 dataframe 并将每个条添加为单独的跟踪。 This way, each trace can get a name that can be grouped in a certain way (which is not possible if adding all bars in a single trace, or at least I could not find a way).这样,每个跟踪都可以获得一个可以以某种方式分组的名称(如果将所有条添加到单个跟踪中,这是不可能的,或者至少我找不到方法)。 Unfortunately, the ordering of the legend is messed up (if you have more then 2 buckets) and there is no way in plotly currently to sort it.不幸的是,图例的顺序是混乱的(如果你有超过 2 个桶),目前 plotly 没有办法对其进行排序。 But that's a minor thing.但那是小事。

The main thing that bothers me is that this could've been so much easier if plotly.express allowed for manual ordering of the bars by a certain column.困扰我的主要事情是,如果 plotly.express 允许按特定列手动排序条形,这可能会容易得多。 Maybe I'll submit that as a suggestion.也许我会把它作为建议提交。

import pandas as pd
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default = "browser"

test_df = pd.DataFrame(
    [[1, 5, 1, 'B'], [3, 3, 1, 'A'], [5, 10, 1, 'B'],
     [2, 8, 2, 'B'], [4, 5, 2, 'A'], [6, 3, 2, 'A']],
    columns = ('ID', 'Value', 'Bucket', 'Type'))
# add named colors to the dataframe based on type
test_df.loc[test_df['Type'] == 'A', 'Color'] = 'Crimson'
test_df.loc[test_df['Type'] == 'B', 'Color'] = 'ForestGreen'
# ensure that the dataframe is sorted by the values
test_df.sort_values('ID', inplace=True)

fig = go.Figure()
# it's tedious to iterate over each item, but only this way we can ensure that everything is correctly ordered and labelled
# Set up legend_show_dict to check if an item should be shown or not. This should be only done for the first occurrence to avoid duplication.
legend_show_dict = {}
for i, row in test_df.iterrows():
    if row['Type'] in legend_show_dict:
        legend_show = legend_show_dict[row['Type']]
    else:
        legend_show = True
        legend_show_dict[row['Type']] = False
    fig.add_trace(
        go.Bar(
            x=[row['Bucket']],
            y=[row['Value']],
            marker_color=row['Color'],
            name=row['Type'],
            legendgroup=row['Type'],
            showlegend=legend_show,
            hovertemplate="<br>".join([
            'ID: ' + str(row['ID']),
            'Value: ' + str(row['Value']),
            'Bucket: ' + str(row['Value']),
            'Type: ' + row['Type'],
            ])
           ))
fig.update_layout(
    xaxis={'categoryorder': 'category ascending', 'title': 'Bucket'},
    yaxis={'title': 'Value'},
    legend={'traceorder': 'normal'}
    )
fig.update_layout(barmode='stack', font_size=20)
fig.show()

This is what it should look like then:这就是它应该看起来的样子:
输出

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM