我试图在matplotlib中复制下面的图像,看起来似乎是我唯一的选择。虽然看起来你不能堆叠barh图,所以我不知道该怎么做
如果你知道有一个更好的python库来绘制这种东西,请告诉我。
这是我能想到的全部内容:
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()
然后我必须使用ax.text单独添加标签,这将是乏味的。理想情况下,我只想指定要插入的部分的宽度,然后用我选择的字符串更新该部分的中心。外面的标签(例如3800)我可以稍后添加自己,它主要是条形部分本身的标签,并以一种很好的方式创建这个堆叠的方法我遇到了问题。你甚至可以用任何方式指定“距离”,即颜色范围吗?
答案 0 :(得分:20)
编辑2:获取更多异构数据。 (我已经离开了上面的方法,因为我发现每个系列使用相同数量的记录更常见)
回答问题的两个部分:
a)barh
为它绘制的所有补丁返回一个句柄容器。您可以使用补丁的坐标来帮助文本位置。
b)按照我之前提到的问题的these two个答案(请参阅Horizontal stacked bar chart in Matplotlib),您可以通过设置“左”输入来水平堆叠条形图。
另外c)处理形状不均匀的数据。
下面是一种处理形状不太均匀的数据的方法,只需单独处理每个段。
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()
这会生成一个类似于此的图表,每个系列中都会显示不同数量的细分。
请注意,这并不是特别有效,因为每个细分使用了对ax.barh
的单独调用。可能存在更有效的方法(例如,通过填充具有零宽度段或纳米值的矩阵)但这可能是特定于问题的并且是一个独特的问题。
编辑:更新以回答问题的两个部分。
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()
您可以沿着这些方向获得结果(注意:我使用的百分比与条宽度无关,因为示例中的关系似乎不清楚):
有关堆叠水平条形图的一些想法,请参阅Horizontal stacked bar chart in Matplotlib。
答案 1 :(得分:3)
pandas.DataFrame
中
'People'
)都没有全部细分(例如某些值为0或NaN
),这也会绘制并正确注释。matplotlib
引擎通过以下方式进行绘制:
pandas.DataFrame.plot.barh
label_text = f'{width}'
进行注释pandas.DataFrame.plot.bar
label_text = f'{height}'
进行注释matplotlib.axes.Axes
或numpy.ndarray
。.patches
方法解压缩matplotlib.patches.Rectangle
对象的列表,每个对象对应堆积条形的每个部分。
.Rectangle
都有提取定义矩形的各种值的方法。.Rectangle
的顺序是从左到右,从下到上,因此,在遍历.Rectangle
时,每个级别的所有.patches
对象都按顺序显示。 / li>
label_text = f'{width:.2f}%'
制作标签,因此可以根据需要添加任何其他文本。import pandas as pd
import numpy as np
# create sample data as shown in the OP
np.random.seed(365)
people = ('A','B','C','D','E','F','G','H')
bottomdata = 3 + 10 * np.random.rand(len(people))
topdata = 3 + 10 * np.random.rand(len(people))
# create the dataframe
df = pd.DataFrame({'Female': bottomdata, 'Male': topdata}, index=people)
# display(df)
Female Male
A 12.41 7.42
B 9.42 4.10
C 9.85 7.38
D 8.89 10.53
E 8.44 5.92
F 6.68 11.86
G 10.67 12.97
H 6.05 7.87
# plot the dataframe with 1 line
ax = df.plot.barh(stacked=True, figsize=(8, 6))
# .patches is everything inside of the chart
for rect in ax.patches:
# Find where everything is located
height = rect.get_height()
width = rect.get_width()
x = rect.get_x()
y = rect.get_y()
# The height of the bar is the data value and can be used as the label
label_text = f'{width:.2f}%' # f'{width:.2f}' to format decimal values
# ax.text(x, y, text)
label_x = x + width / 2
label_y = y + height / 2
# only plot labels greater than given width
if width > 0:
ax.text(label_x, label_y, label_text, ha='center', va='center', fontsize=8)
# move the legend
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.)
# add labels
ax.set_ylabel("People", fontsize=18)
ax.set_xlabel("Percent", fontsize=18)
plt.show()
# set one of the dataframe values to 0
df.iloc[4, 1] = 0
df
的正确位置。