如何在 Matplotlib 中为 Stack Percentage Barplot 添加注释
How to add annotations to Stack Percentage Barplot in Matplotlib
我想使用 matplotlib 向堆积条形图添加值。到目前为止,我已经能够创建堆叠条形图,但我对如何添加注释感到困惑。
已经回答了类似的问题 ,但是对于 ggplot。
我想要一个类似的输出,不是整个图形,而是中间的注释。
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
data = {'Range':['<10','>10', '>= 20', '<10','>10', '>= 20', '<10','>10', '>= 20'],
'Price':[50,25,25,70,20,10,80,10,10]
'Value':[100,50,50,140,40,20,160,20,20]}
df1 = pd.DataFrame(data)
b1 = df1[(df1['Range'] == '<10']['Price']
b2 = df1[df1['Range'] == '>10']['Price']
b3 = df1[df1['Range'] == '>= 20']['Price']
totals = [i+j+k for i,j,k in zip(b1,b2,b3)]
greenBars = [i / j * 100 for i,j in zip(b1, totals)]
orangeBars = [i / j * 100 for i,j in zip(b2, totals)]
blueBars = [i / j * 100 for i,j in zip(b3, totals)]
barWidth = 0.5
names = ('low', 'medium', 'high')
r = [0,1,2]
plt.bar(r, greenBars, color='#b5ffb9', edgecolor='white', width=barWidth, label = '$<10')
plt.bar(r, orangeBars, bottom=greenBars, color='#f9bc86', edgecolor='white', width=barWidth, label = '$>10')
plt.bar(r, blueBars, bottom=[i+j for i,j in zip(greenBars, orangeBars)], color='#a3acff', edgecolor='white', width=barWidth, label = '$>=20')
plt.xticks(r, names)
plt.xlabel("group")
plt.legend(loc='upper left', bbox_to_anchor=(1,1), ncol=1)
plt.show()
在上面添加了代码以创建堆叠图。
期望的输出:
对于 Low 类别,通过从列 Value
中提取值为 100、50 和 50
的值在堆栈上添加注释
对于中值,值为 140、40 和 20。
高值为 160、20 和 20。
- 这个答案将根据 Quang Hoang
中的代码简化绘图
- 可以通过从
ax.patches
中提取条形图位置来注释条形图。
- 补丁数据不包含与数据帧对应的标签,因此关联不同的数据值集成为定制过程。
- 为了用
Value
而不是Price
进行注释,需要有一种方法来关联相应的值。
- 字典不起作用,因为有重复值
- 为
Value
制作一个旋转数据框,为 Price
制作一个相应的数据框。这将确保相应的数据位于同一位置。
col_idx
和 row_idx
将与 .iloc
一起使用以在 df_value
中找到正确的值,用它来注释图。
col_idx
和 row_idx
都可以在 if i%3 == 0
中重置或更新,因为有 3 个条和 3 个线段,但是,如果条和线段的数量不同,需要不同的重置条件。
import pandas as pd
import matplotlib.pyplot as plt
# create the dataframe
data = {'Range':['<10','>10', '>= 20', '<10','>10', '>= 20', '<10','>10', '>= 20'],
'Price':[50,25,25,70,20,10,80,10,10],
'Value':[100,50,50,140,40,20,160,20,20]}
df1 = pd.DataFrame(data)
# pivot the price data
df_price = df1.assign(idx=df1.groupby('Range').cumcount()).pivot(index='idx', columns='Range', values='Price')
Range <10 >10 >= 20
idx
0 50 25 25
1 70 20 10
2 80 10 10
# pivot the value data
df_value = df1.assign(idx=df1.groupby('Range').cumcount()).pivot(index='idx', columns='Range', values='Value')
Range <10 >10 >= 20
idx
0 100 50 50
1 140 40 20
2 160 20 20
# set colors
colors = ['#b5ffb9', '#f9bc86', '#a3acff']
# plot the price
ax = df_price.plot.bar(stacked=True, figsize=(8, 6), color=colors, ec='w')
# label the x-axis
plt.xticks(ticks=range(3), labels=['low', 'med', 'high'], rotation=0)
# x-axis title
plt.xlabel('group')
# position the legend
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
# annotate the bar segments
# col and row iloc indices for df_value
col_idx = 0
row_idx = 0
# iterate through each bar patch from ax
for i, p in enumerate(ax.patches, 1):
left, bottom, width, height = p.get_bbox().bounds
v = df_value.iloc[row_idx, col_idx]
if width > 0:
ax.annotate(f'{v:0.0f}', xy=(left+width/2, bottom+height/2), ha='center', va='center')
# use this line to add commas for thousands
# ax.annotate(f'{v:,}', xy=(left+width/2, bottom+height/2), ha='center', va='center')
row_idx += 1
if i%3 == 0: # there are three bars, so update the indices
col_idx += 1
row_idx = 0
我想使用 matplotlib 向堆积条形图添加值。到目前为止,我已经能够创建堆叠条形图,但我对如何添加注释感到困惑。
已经回答了类似的问题
我想要一个类似的输出,不是整个图形,而是中间的注释。
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
data = {'Range':['<10','>10', '>= 20', '<10','>10', '>= 20', '<10','>10', '>= 20'],
'Price':[50,25,25,70,20,10,80,10,10]
'Value':[100,50,50,140,40,20,160,20,20]}
df1 = pd.DataFrame(data)
b1 = df1[(df1['Range'] == '<10']['Price']
b2 = df1[df1['Range'] == '>10']['Price']
b3 = df1[df1['Range'] == '>= 20']['Price']
totals = [i+j+k for i,j,k in zip(b1,b2,b3)]
greenBars = [i / j * 100 for i,j in zip(b1, totals)]
orangeBars = [i / j * 100 for i,j in zip(b2, totals)]
blueBars = [i / j * 100 for i,j in zip(b3, totals)]
barWidth = 0.5
names = ('low', 'medium', 'high')
r = [0,1,2]
plt.bar(r, greenBars, color='#b5ffb9', edgecolor='white', width=barWidth, label = '$<10')
plt.bar(r, orangeBars, bottom=greenBars, color='#f9bc86', edgecolor='white', width=barWidth, label = '$>10')
plt.bar(r, blueBars, bottom=[i+j for i,j in zip(greenBars, orangeBars)], color='#a3acff', edgecolor='white', width=barWidth, label = '$>=20')
plt.xticks(r, names)
plt.xlabel("group")
plt.legend(loc='upper left', bbox_to_anchor=(1,1), ncol=1)
plt.show()
在上面添加了代码以创建堆叠图。 期望的输出:
对于 Low 类别,通过从列 Value
中提取值为 100、50 和 50
对于中值,值为 140、40 和 20。
高值为 160、20 和 20。
- 这个答案将根据 Quang Hoang 中的代码简化绘图
- 可以通过从
ax.patches
中提取条形图位置来注释条形图。- 补丁数据不包含与数据帧对应的标签,因此关联不同的数据值集成为定制过程。
- 为了用
Value
而不是Price
进行注释,需要有一种方法来关联相应的值。- 字典不起作用,因为有重复值
- 为
Value
制作一个旋转数据框,为Price
制作一个相应的数据框。这将确保相应的数据位于同一位置。
col_idx
和row_idx
将与.iloc
一起使用以在df_value
中找到正确的值,用它来注释图。col_idx
和row_idx
都可以在if i%3 == 0
中重置或更新,因为有 3 个条和 3 个线段,但是,如果条和线段的数量不同,需要不同的重置条件。
import pandas as pd
import matplotlib.pyplot as plt
# create the dataframe
data = {'Range':['<10','>10', '>= 20', '<10','>10', '>= 20', '<10','>10', '>= 20'],
'Price':[50,25,25,70,20,10,80,10,10],
'Value':[100,50,50,140,40,20,160,20,20]}
df1 = pd.DataFrame(data)
# pivot the price data
df_price = df1.assign(idx=df1.groupby('Range').cumcount()).pivot(index='idx', columns='Range', values='Price')
Range <10 >10 >= 20
idx
0 50 25 25
1 70 20 10
2 80 10 10
# pivot the value data
df_value = df1.assign(idx=df1.groupby('Range').cumcount()).pivot(index='idx', columns='Range', values='Value')
Range <10 >10 >= 20
idx
0 100 50 50
1 140 40 20
2 160 20 20
# set colors
colors = ['#b5ffb9', '#f9bc86', '#a3acff']
# plot the price
ax = df_price.plot.bar(stacked=True, figsize=(8, 6), color=colors, ec='w')
# label the x-axis
plt.xticks(ticks=range(3), labels=['low', 'med', 'high'], rotation=0)
# x-axis title
plt.xlabel('group')
# position the legend
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
# annotate the bar segments
# col and row iloc indices for df_value
col_idx = 0
row_idx = 0
# iterate through each bar patch from ax
for i, p in enumerate(ax.patches, 1):
left, bottom, width, height = p.get_bbox().bounds
v = df_value.iloc[row_idx, col_idx]
if width > 0:
ax.annotate(f'{v:0.0f}', xy=(left+width/2, bottom+height/2), ha='center', va='center')
# use this line to add commas for thousands
# ax.annotate(f'{v:,}', xy=(left+width/2, bottom+height/2), ha='center', va='center')
row_idx += 1
if i%3 == 0: # there are three bars, so update the indices
col_idx += 1
row_idx = 0