【发布时间】:2017-12-02 18:56:51
【问题描述】:
我正在尝试在 MatPlotLib 中创建一个堆叠条形图,顶部和底部有两个不同的 x 标签。上面的应该有一个与条本身宽度相同的边界框。
剧情不太对
这就是我创建标签的方式:
plt.tick_params(axis="both", left=False, bottom=False, labelleft=False)
plt.xticks(ind, diagram.keys())
ax.set_frame_on(False)
for label, x in zip([q[1] for q in diagram.values()], ind):
ax.text(
x, 1.05, '{:4.0%}'.format(label),
ha="center", va="center",
bbox={"facecolor": "blue", "pad": 3}
)
diagram 是像{bottom-label: [[contents], top-label]} 这样的字典
所以我想我的问题归结为:如何操作文本对象的边界框?
非常感谢!
根据要求,一个可运行的例子:
import matplotlib.pyplot as plt
import numpy as np
def stacked_bar_chart(
diagram, title="example question", img_name="test_image", width=0.7, clusters=None, show_axes=True,
show_legend=True, show_score=True):
"""
Builds one or multiple scaled stacked bar charts for grade
distributions. saves image as png.
:param show_score: whether the score should be shown on top
:param show_legend: whether the legend should be shown
:param show_axes: whether question name should be shown on bottom
:param clusters: indices of clusters to be displayed.
:param width: the width of the bars as fraction of available space
:param title: diagram title
:param img_name: output path
:param diagram: dictionary: {x-label: [[grade distribution], score]}
:return: nothing.
"""
grades = {
"sehr gut": "#357100",
"gut": "#7fb96a",
"befriedigend": "#fdd902",
"ausreichend": "#f18d04",
"mangelhaft": "#e3540e",
"ungenügend": "#882d23"
}
# select clusters
if clusters is not None:
diagram = {i: diagram[i] for i in clusters}
# normalize score distribution => sum of votes = 1.0
normalized = []
for question in diagram.values():
s = sum(question[0])
normalized.append([x / s for x in question[0]])
# transpose dict values (score distributions) to list of lists
transformed = list(map(list, zip(*normalized)))
# input values for diagram generation
n = len(diagram) # number of columns
ind = np.arange(n) # x values for bar center
base = [0] * n # lower bounds for individual color set
bars = []
fig, ax = plt.subplots()
# loop over grades
for name, grade in zip(grades.keys(), transformed):
assert len(grade) == n, \
"something went wrong in plotting grade stack " + img_name
bar = plt.bar(ind, grade, width=width, color=grades[name], bottom=base)
bars.append(bar)
# loop over bars
for i, (rect, score) in enumerate(zip(bar, grade)):
# update lower bound for next bar section
base[i] += grade[i]
# label with percentage
# TODO text color white
ax.text(
rect.get_x() + width / 2, rect.get_height() / 2 + rect.get_y(), "{0:.0f}%".format(score * 100),
va="center", ha="center")
# label diagram
plt.suptitle(title)
if show_axes:
plt.tick_params(axis="both", left=False, bottom=False, labelleft=False)
plt.xticks(ind, diagram.keys())
ax.set_frame_on(False)
else:
plt.tick_params(axis="both", left=False, bottom=False, labelleft=False, labelbottom=False)
plt.axis("off")
# show score label above
if show_score:
for label, x in zip([q[1] for q in diagram.values()], ind):
ax.text(
x, 1.05, '{:4.0%}'.format(label),
ha="center", va="center",
bbox={"facecolor": "blue", "pad": 3}
)
# create legend
if show_legend:
plt.legend(
reversed(bars), reversed([*grades]),
bbox_to_anchor=(1, 1), borderaxespad=0)
# save file
plt.show()
diagram = {
"q1": [[1, 2, 3, 4, 5, 6], 0.6],
"q2": [[2, 3, 1, 2, 3, 1], 0.4]
}
stacked_bar_chart(diagram)
【问题讨论】:
-
这个比较麻烦,见this question,它想为标题框做这个。当然,可以根据条宽调整它。另一方面,人们可能宁愿在轴上放置一些文本,并在其背景中绘制一个具有所需尺寸的矩形。你想往哪个方向走?
-
我更喜欢前一种变体。我已经探索过绘制矩形的选项,但由于我不记得的原因失败了……再说一次,我是寻求建议的人,所以我愿意接受你认为最好的任何事情。
-
我可以看到我能做什么,但你介意提供一个minimal reproducible example(不要使用你的真实数据,只是我可以复制、粘贴和运行的东西)。
-
我试过了。还是有点多,有趣的部分在评论附近
# show score label above -
那不是minimal reproducible example。我创建了自己的来回答这个问题。请注意,您不能指望人们为您工作。
标签: python matplotlib