如何调整 Plotly 条形高度并仅显示条形边缘(在子图中)?

How do I resize my Plotly bar height and show only bar’s edge (in subplot)?

这是我第一次涉足 Plotly。与 matplotlib 和 bokeh 相比,我喜欢它的易用性。但是,我在如何美化我的情节方面遇到了一些基本问题。首先,这是下面的代码(功能齐全,只需复制和粘贴!):

import plotly.express as px
from plotly.subplots import make_subplots
import plotly as py
import pandas as pd
from plotly import tools

d = {'Mkt_cd': ['Mkt1','Mkt2','Mkt3','Mkt4','Mkt5','Mkt1','Mkt2','Mkt3','Mkt4','Mkt5'],
       'Category': ['Apple','Orange','Grape','Mango','Orange','Mango','Apple','Grape','Apple','Orange'],
           'CategoryKey': ['Mkt1Apple','Mkt2Orange','Mkt3Grape','Mkt4Mango','Mkt5Orange','Mkt1Mango','Mkt2Apple','Mkt3Grape','Mkt4Apple','Mkt5Orange'],
            'Current': [15,9,20,10,20,8,10,21,18,14],
           'Goal': [50,35,21,44,20,24,14,29,28,19]
     }
dataset  = pd.DataFrame(d)

grouped = dataset.groupby('Category', as_index=False).sum()
data = grouped.to_dict(orient='list')
v_cat = grouped['Category'].tolist()
v_current = grouped['Current']
v_goal = grouped['Goal']
fig1 = px.bar(dataset, x = v_current, y = v_cat, orientation = 'h',
              color_discrete_sequence = ["#ff0000"],height=10)
fig2 = px.bar(dataset, x = v_goal, y = v_cat, orientation = 'h',height=15)

trace1 = fig1['data'][0]
trace2 = fig2['data'][0]
fig = make_subplots(rows = 1, cols = 1, shared_xaxes=True, shared_yaxes=True)
fig.add_trace(trace2, 1, 1)
fig.add_trace(trace1, 1, 1)
fig.update_layout(barmode = 'overlay')
fig.show()

这是输出:

问题1:如何使v_current(红色条中所示)的宽度变小?就像,它的高度应该更小,因为这是一个单杠。我将 trace1 的高度添加为 10,将 trace2 的高度添加为 15,但它们仍然显示相同的高度。

问题 2:有没有办法让 v_goal(显示在蓝色条中)只显示它的右边缘,而不是填充条?是这样的:

如果您注意到了,我还在每个类别下添加了一行。有没有快速的方法来添加这个?不是交易破坏者,只是奖金。我正在尝试做的其他事情是添加动画等,但那是其他时间!

提前感谢您的回答!

您可以使用 Plotly Express 然后像@vestland 描述的那样直接访问图形对象,但我个人更喜欢使用 graph_objects 在一个地方进行所有更改。

我还要指出,由于您在一张图表中堆叠条形图,因此不需要子图。您可以使用 fig = go.Figure() 创建一个 graph_object 并添加轨迹以获得堆积条,类似于您已经执行的操作。

对于问题1,如果您使用的是go.Bar(),您可以传递一个宽度参数。但是,这是以位置轴为单位,并且由于您的 y 轴是分类的,width=1 将填充整个类别,因此我为红色条选择了 width=0.25,而 width=0.3(稍大)蓝色条,因为那似乎是你的意图。

对于问题2,唯一想到的就是hack。将条形分成两部分(一个高度 = 原始高度 - 1),并将其不透明度设置为 0 以使其透明。然后在透明条的顶部放置高度为 1 的下条。

如果您不希望轨迹显示在图例中,您可以通过将 showlegend=False 传递到 fig.add_trace 为每个柱单独​​设置此项,或者通过将 fig.add_trace 完全隐藏图例=16=] 到 fig.update_layout 方法。

import plotly.express as px
import plotly.graph_objects as go
# from plotly.subplots import make_subplots
import plotly as py
import pandas as pd
from plotly import tools

d = {'Mkt_cd': ['Mkt1','Mkt2','Mkt3','Mkt4','Mkt5','Mkt1','Mkt2','Mkt3','Mkt4','Mkt5'],
       'Category': ['Apple','Orange','Grape','Mango','Orange','Mango','Apple','Grape','Apple','Orange'],
           'CategoryKey': ['Mkt1Apple','Mkt2Orange','Mkt3Grape','Mkt4Mango','Mkt5Orange','Mkt1Mango','Mkt2Apple','Mkt3Grape','Mkt4Apple','Mkt5Orange'],
            'Current': [15,9,20,10,20,8,10,21,18,14],
           'Goal': [50,35,21,44,20,24,14,29,28,19]
     }
dataset  = pd.DataFrame(d)

grouped = dataset.groupby('Category', as_index=False).sum()
data = grouped.to_dict(orient='list')
v_cat = grouped['Category'].tolist()
v_current = grouped['Current']
v_goal = grouped['Goal']

fig = go.Figure()

## you have a categorical plot and the units for width are in position axis units
## therefore width = 1 will take up the entire allotted space
## a width value of less than 1 will be the fraction of the allotted space
fig.add_trace(go.Bar(
    x=v_current,
    y=v_cat,
    marker_color="#ff0000",
    orientation='h',
    width=0.25
    ))

## you can show the right edge of the bar by splitting it into two bars
## with the majority of the bar being transparent (opacity set to 0)
fig.add_trace(go.Bar(
    x=v_goal-1,
    y=v_cat,
    marker_color="#ffffff",
    opacity=0,
    orientation='h',
    width=0.30,
    ))

fig.add_trace(go.Bar(
    x=[1]*len(v_cat),
    y=v_cat,
    marker_color="#1f77b4",
    orientation='h',
    width=0.30,
    ))

fig.update_layout(barmode='relative')
fig.show()

运行 plotly.express 将 return 一个 plotly.graph_objs._figure.Figure 对象。 plotly.graph_objects 运行 go.Figure() 以及 go.Bar() 也是如此。所以在使用 plotly express 构建图形后,您可以通过引用直接向图形添加线条或痕迹,如:

fig['data'][0].width = 0.4

这正是您设置条形宽度所需要的。您可以轻松地将其与 plotly express 结合使用:

代码 1

fig = px.bar(grouped, y='Category', x = ['Current'],
             orientation = 'h', barmode='overlay', opacity = 1,
             color_discrete_sequence = px.colors.qualitative.Plotly[1:])
fig['data'][0].width = 0.4

情节 1

为了获得指示目标级别的条形或形状,您可以使用 DerekO 描述的方法,或者您可以使用:

for i, g in enumerate(grouped.Goal):
    fig.add_shape(type="rect",
                    x0=g+1, y0=grouped.Category[i], x1=g, y1=grouped.Category[i],
                    line=dict(color='#636EFA', width = 28))

完整代码:

import plotly.express as px
from plotly.subplots import make_subplots
import plotly as py
import pandas as pd
from plotly import tools

d = {'Mkt_cd': ['Mkt1','Mkt2','Mkt3','Mkt4','Mkt5','Mkt1','Mkt2','Mkt3','Mkt4','Mkt5'],
       'Category': ['Apple','Orange','Grape','Mango','Orange','Mango','Apple','Grape','Apple','Orange'],
           'CategoryKey': ['Mkt1Apple','Mkt2Orange','Mkt3Grape','Mkt4Mango','Mkt5Orange','Mkt1Mango','Mkt2Apple','Mkt3Grape','Mkt4Apple','Mkt5Orange'],
            'Current': [15,9,20,10,20,8,10,21,18,14],
           'Goal': [50,35,21,44,20,24,14,29,28,19]
     }
dataset  = pd.DataFrame(d)

grouped = dataset.groupby('Category', as_index=False).sum()

fig = px.bar(grouped, y='Category', x = ['Current'],
             orientation = 'h', barmode='overlay', opacity = 1,
             color_discrete_sequence = px.colors.qualitative.Plotly[1:])

fig['data'][0].width = 0.4
fig['data'][0].marker.line.width = 0

for i, g in enumerate(grouped.Goal):
    fig.add_shape(type="rect",
                    x0=g+1, y0=grouped.Category[i], x1=g, y1=grouped.Category[i],
                    line=dict(color='#636EFA', width = 28))
f = fig.full_figure_for_development(warn=False)
fig.show()