我想在Python上用plotly做一些与图片非常相似的事情。我试图找到一种使用子图和共享轴的方法,但找不到正确的方法。是否可以将条形图的 x 轴与表格的列标题共享?
具有共享 x 轴的条形图
import plotly.express as px
import pandas as pd
import numpy as np
df = pd.DataFrame({"year": range(2011, 2022)}).assign(
pct=lambda d: np.random.uniform(-0.08, 0.08, len(d))
)
px.bar(df, x="year", y="pct").add_traces(
px.bar(df, x="year", y=np.full(len(df), 1), text="pct")
.update_traces(
yaxis="y2",
marker={"line": {"color": "black", "width": 1.5}, "color": "#E5ECF6"},
texttemplate="%{text:,.2%}",
)
.data
).update_layout(
yaxis={"domain": [0.2, 1], "tickformat": ",.2%"},
yaxis2={"domain": [0, 0.1], "visible": False},
xaxis={"title": "", "dtick": 1},
)
基于 matplotlib 的 this 答案,我能够使用 Plotly 中的注释进行类似的操作。您可以通过添加实际行而不是
|
来改进,但这对于我的目的来说效果很好。这样做的好处是它是交互式的,当您移动或调整大小时,标签会随着栏左右移动。
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from itertools import groupby
def test_table():
data_table = pd.DataFrame({'Room':['Room A']*4 + ['Room B']*4,
'Shelf':(['Shelf 1']*3 + ['Shelf 2']*1)*2,
'Staple':['Milk','Water','Sugar','Honey','Wheat','Corn','Chicken','Cow'],
'Quantity':[10,20,5,6,4,7,2,1],
'Ordered':np.random.randint(0,10,8)
})
return data_table
def add_line(fig, xpos, ypos):
# print(xpos, ypos)
fig.add_annotation(
x=xpos,
xref="x",
xanchor="center",
y=ypos,
yref="paper",
text="|",
showarrow=False
)
def add_text(fig, text, xpos, ypos, xanchor="center"):
# print(text, xpos, ypos)
fig.add_annotation(
x=xpos,
xref="x",
xanchor=xanchor,
y=ypos,
yref="paper",
text=text,
showarrow=False
)
def label_len(my_index, level):
# returns list of tuple: [(level label, count)]
labels = my_index.get_level_values(level)
return [(k, sum(1 for i in g)) for k,g in groupby(labels)]
def label_group_bar_table(fig, df, y_scale=0.1):
for ii, level in enumerate(range(df.index.nlevels)[::-1]):
pos = 0
ypos = -y_scale * (ii + 1)
lv_name = df.index.names[level]
add_text(fig, f"{lv_name}: ", -0.5, ypos, xanchor="right")
add_line(fig, -0.5, ypos)
for label, rpos in label_len(df.index, level):
lbpos = (pos + 0.5 * (rpos - 1))
lnpos = (pos + rpos - 0.5)
# print(lbpos, lnpos, ypos, label)
pos += rpos
add_text(fig, label, lbpos, ypos)
add_line(fig, lnpos, ypos)
return fig
df = test_table().groupby(['Room','Shelf','Staple']).sum()
# display(df)
fig = go.Figure()
fig.add_trace(
go.Bar(y=df["Quantity"], name="Quantity")
)
fig.add_trace(
go.Bar(y=df["Ordered"], name="Ordered")
)
fig.update_layout(
barmode="stack",
xaxis_showticklabels=False,
margin=dict(b=100)
)
fig = label_group_bar_table(fig, df)
fig.show()