我在数据框中有要用堆叠条形图绘制的数据:
test_df = pd.DataFrame([[1, 5, 1, 'A'], [2, 10, 1, 'B'], [3, 3, 1, 'A']], columns = ('ID', 'Value', 'Bucket', 'Type'))
如果我使用Plotly Express进行绘图,我会得到彼此堆叠的条形图,并且排序正确(基于索引):
fig = px.bar(test_df, x='Bucket', y='Value', barmode='stack')
但是,我想根据类型对数据进行着色,因此我选择
fig = px.bar(test_df, x='Bucket', y='Value', barmode='stack', color='Type')
这是可行的,除了现在的排序是混乱的,因为所有的条形图现在是按类型分组的。我浏览了Plotly Express的文档,但找不到一种方法来独立指定栏的顺序。关于如何做到这一点,有什么建议吗?
我在这里找到了这个,但是场景有点不同,这里提到的选项似乎对我没有帮助:How to disable plotly express from grouping bars based on color?
编辑:这是正确的方向,但不是使用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"]))
输出:
尽管如此,我还是更喜欢Express版本,因为在那里很多事情都更容易处理(Legend、Hover属性等)。
发布于 2021-03-08 23:07:39
我能理解你的问题的唯一方法是,你不希望B
堆叠在A
之上,而是相反。如果是这样的话,你就可以得到你想要的东西:
fig.data = fig.data[::-1]
fig.layout.legend.traceorder = 'reversed'
以下是一些详细信息:
fig.data = fig.data[::-1]
只是颠倒了轨迹在fig.data
中出现的顺序,并最终出现在绘制的图形本身中。然而,这也会颠倒图例的顺序。因此,如果没有fig.layout.legend.traceorder = 'reversed'
,结果将是:
因此,完整的变通方法如下所示:
fig.data = fig.data[::-1]
fig.layout.legend.traceorder = 'reversed'
完整代码:
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()
发布于 2021-06-10 14:12:52
好吧,很抱歉在这个问题上拖延了很长时间,但我最终还是解决了这个问题。
我的解决方案可能不是最直接的,但它确实有效。
基本思想是使用graph_objects而不是express,然后在数据帧上迭代,并将每个条作为单独的跟踪添加。这样,每个跟踪都可以获得一个可以以某种方式分组的名称(如果在一个跟踪中添加所有条,这是不可能的,或者至少我找不到一种方法)。不幸的是,图例的排序是混乱的(如果你有2个以上的存储桶),并且目前没有办法对其进行排序。但这只是一件小事。
最让我困扰的是,如果plotly.express允许按特定列对栏进行手动排序,这可能会容易得多。也许我会把它作为一个建议来提交。
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()
下面是它应该是什么样子的:
https://stackoverflow.com/questions/66527861
复制相似问题