Python matplotlib 中的堆栈条形图并为每个部分添加标签(和建议)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21397549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
stack bar plot in matplotlib and add label to each section (and suggestions)
提问by Griff
I am trying to replicate the following image in matplotlib and it seems barh is my only option. Though it appears that you can't stack barh graphs so I don't know what to do
我正在尝试在 matplotlib 中复制以下图像,看来 barh 是我唯一的选择。虽然看起来你不能堆叠 barh 图所以我不知道该怎么做


If you know of a better python library to draw this kind of thing, please let me know.
如果你知道一个更好的 python 库来绘制这种东西,请告诉我。
This is all I could come up with as a start:
这是我能想出的所有开始:
import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as plt
people = ('A','B','C','D','E','F','G','H')
y_pos = np.arange(len(people))
bottomdata = 3 + 10 * np.random.rand(len(people))
topdata = 3 + 10 * np.random.rand(len(people))
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)
ax.barh(y_pos, bottomdata,color='r',align='center')
ax.barh(y_pos, topdata,color='g',align='center')
ax.set_yticks(y_pos)
ax.set_yticklabels(people)
ax.set_xlabel('Distance')
plt.show()
I would then have to add labels individually using ax.text which would be tedious. Ideally I would like to just specify the width of the part to be inserted then it updates the center of that section with a string of my choosing. The labels on the outside (e.g. 3800) I can add myself later, it is mainly the labeling over the bar section itself and creating this stacked method in a nice way I'm having problems with. Can you even specify a 'distance' i.e. span of color in any way?
然后我将不得不使用 ax.text 单独添加标签,这会很乏味。理想情况下,我只想指定要插入的部分的宽度,然后使用我选择的字符串更新该部分的中心。外面的标签(例如 3800)我可以稍后添加,它主要是在条形部分本身上的标签,并以一种很好的方式创建了这种堆叠方法,我遇到了问题。您甚至可以以任何方式指定“距离”,即颜色范围吗?


回答by Bonlenfum
Edit 2: for more heterogeneous data. (I've left the above method since I find it more usual to work with the same number of records per series)
编辑 2:用于更多异构数据。(我已经放弃了上述方法,因为我发现每个系列使用相同数量的记录更常见)
Answering the two parts of the question:
回答问题的两个部分:
a) barhreturns a container of handles to all the patches that it drew. You can use the coordinates of the patches to aid the text positions.
a)barh返回它绘制的所有补丁的句柄容器。您可以使用补丁的坐标来辅助文本位置。
b) Following thesetwoanswers to the question that I noted before (see Horizontal stacked bar chart in Matplotlib), you can stack bar graphs horizontally by setting the 'left' input.
b) 按照我之前提到的问题的这两个答案(参见Matplotlib 中的水平堆积条形图),您可以通过设置“左”输入来水平堆积条形图。
and additionally c) handling data that is less uniform in shape.
另外 c) 处理形状不太统一的数据。
Below is one way you could handle data that is less uniform in shape is simply to process each segment independently.
下面是处理形状不太统一的数据的一种方法,即简单地独立处理每个段。
import numpy as np
import matplotlib.pyplot as plt
# some labels for each row
people = ('A','B','C','D','E','F','G','H')
r = len(people)
# how many data points overall (average of 3 per person)
n = r * 3
# which person does each segment belong to?
rows = np.random.randint(0, r, (n,))
# how wide is the segment?
widths = np.random.randint(3,12, n,)
# what label to put on the segment
labels = xrange(n)
colors ='rgbwmc'
patch_handles = []
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)
left = np.zeros(r,)
row_counts = np.zeros(r,)
for (r, w, l) in zip(rows, widths, labels):
print r, w, l
patch_handles.append(ax.barh(r, w, align='center', left=left[r],
color=colors[int(row_counts[r]) % len(colors)]))
left[r] += w
row_counts[r] += 1
# we know there is only one patch but could enumerate if expanded
patch = patch_handles[-1][0]
bl = patch.get_xy()
x = 0.5*patch.get_width() + bl[0]
y = 0.5*patch.get_height() + bl[1]
ax.text(x, y, "%d%%" % (l), ha='center',va='center')
y_pos = np.arange(8)
ax.set_yticks(y_pos)
ax.set_yticklabels(people)
ax.set_xlabel('Distance')
plt.show()
Which produces a graph like this
, with a different number of segments present in each series.
这会产生这样的图表
,每个系列中存在不同数量的段。
Note that this is not particularly efficient since each segment used an individual call to ax.barh. There may be more efficient methods (e.g. by padding a matrix with zero-width segments or nan values) but this likely to be problem-specific and is a distinct question.
请注意,这并不是特别有效,因为每个段都使用了对 的单独调用ax.barh。可能有更有效的方法(例如,通过用零宽度段或 nan 值填充矩阵),但这可能是特定于问题的,并且是一个独特的问题。
Edit: updated to answer both parts of the question.
编辑:更新以回答问题的两个部分。
import numpy as np
import matplotlib.pyplot as plt
people = ('A','B','C','D','E','F','G','H')
segments = 4
# generate some multi-dimensional data & arbitrary labels
data = 3 + 10* np.random.rand(segments, len(people))
percentages = (np.random.randint(5,20, (len(people), segments)))
y_pos = np.arange(len(people))
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)
colors ='rgbwmc'
patch_handles = []
left = np.zeros(len(people)) # left alignment of data starts at zero
for i, d in enumerate(data):
patch_handles.append(ax.barh(y_pos, d,
color=colors[i%len(colors)], align='center',
left=left))
# accumulate the left-hand offsets
left += d
# go through all of the bar segments and annotate
for j in xrange(len(patch_handles)):
for i, patch in enumerate(patch_handles[j].get_children()):
bl = patch.get_xy()
x = 0.5*patch.get_width() + bl[0]
y = 0.5*patch.get_height() + bl[1]
ax.text(x,y, "%d%%" % (percentages[i,j]), ha='center')
ax.set_yticks(y_pos)
ax.set_yticklabels(people)
ax.set_xlabel('Distance')
plt.show()
You can achieve a result along these lines (note: the percentages I used have nothing to do with the bar widths, as the relationship in the example seems unclear):
您可以沿着这些路线获得结果(注意:我使用的百分比与条形宽度无关,因为示例中的关系似乎不清楚):


See Horizontal stacked bar chart in Matplotlibfor some ideas on stacking horizontal bar plots.
有关堆叠水平条形图的一些想法,请参阅Matplotlib 中的水平堆叠条形图。

