python - 如何使用相关列值自定义 pandas barplot 文本注释?
问题描述
我正在尝试使用注释上的自定义文本构建堆叠条形图。条形图是使用商店位置列表的“morning_sales”和“afternoon_sales”条目构建的,我想为每个盒子构建一个自定义标签以显示盒子的高度和另一列的相关值(在这个情况下,将“morning_staff”与“morning_sales”匹配,将“afternoon_staff”与“afternoon_sales”匹配)。
我的方法有效,但依赖于知道条形图矩形的顺序......我担心如果我对条形图或相关操作进行任何重新排序,事情可能会分崩离析。谁能推荐一个更好的方法来做到这一点?请注意,这是一个“虚拟”数据帧;我的真实数据集是几十万行。
我不确定是否有办法使用“handles, labels = ax.get_legend_handles_labels()”方法提取文本?
这是代码:
import pandas as pd
data = {'location': ['Toronto', 'Vancouver', 'Edmonton', 'Calgary'],
'morning_staff': [3, 12, 25, 6],
'afternoon_staff': [2, 8, None, 8],
'morning_sales': [8000, 25000, 40000, 15000],
'afternoon_sales': [4000, 15000, None, 6000]
}
df = pd.DataFrame(data, columns = ['location', 'morning_staff', 'afternoon_staff', 'morning_sales', 'afternoon_sales' ])
# > Drop 'Calgary' from plot dataset and extract columns for plotting
df_plot = df.loc[df['location'] != 'Calgary', ['location', 'morning_sales', 'afternoon_sales']]
ax = df_plot.plot.bar(x='location', stacked=True, figsize=(8,6), colormap='tab10', fontsize=14)
# Add an annotation to each bar -> Showing staff required for sales
col_tags = ['morning_staff', 'afternoon_staff']
locations = df_plot['location'].tolist()
bar_labels = []
for col_tag in col_tags: # morning_sales, afternoon_sales
for location in locations:
idx = df.loc[df['location'] == location].index[0]
bar_label = df.loc[idx, col_tag].item()
bar_labels.append(bar_label)
rects = ax.patches
for rect, bar_label in zip(rects, bar_labels):
width, height = rect.get_width(), rect.get_height()
if ((height != 0) & (bar_label != np.nan)) :
x, y = rect.get_xy()
text = f'{int(bar_label)}: {int(height)}'
ax.text(x+width/2,
y+height/2,
text,
horizontalalignment='center',
verticalalignment='center',
fontsize=12)
解决方案
import pandas as pd
data = {'location': ['Toronto', 'Vancouver', 'Edmonton', 'Calgary'],
'morning_staff': [3, 12, 25, 6],
'afternoon_staff': [2, 8, None, 8],
'morning_sales': [8000, 25000, 40000, 15000],
'afternoon_sales': [4000, 15000, None, 6000]
}
df=pd.DataFrame.from_dict(data)
df.set_index('location',inplace=True)
df['afternoon_staff']=df['afternoon_staff'].astype('Int64')
print(df)
df_plot=df.iloc[:-1,:]#skip the last row Calgary using indexing
df_plot.iloc[:,2:].plot(kind='bar',stacked=True,,colormap='tab10')
for i in range(len(df_plot)):
morning_lable=str(df_plot['morning_staff'][i])+':'+str(df_plot['morning_sales'][i])
afternoon_lable=str(df_plot['afternoon_staff'][i])+':'+str(df_plot['afternoon_sales'][i])
plt.annotate(morning_lable,(i-0.2,df_plot['morning_sales'][i]/2))
plt.annotate(afternoon_lable,(i-0.2,df_plot['morning_sales'][i]+df_plot['afternoon_sales'][i]/2))
plt.tight_layout()
输出:
morning_staff afternoon_staff morning_sales afternoon_sales
location
Toronto 3 2 8000 4000.0
Vancouver 12 8 25000 15000.0
Edmonton 25 <NA> 40000 NaN
Calgary 6 8 15000 6000.0
推荐阅读
- python - 使用正则表达式时如何避免特定模式?
- c++ - CPP程序查找nxn阶矩阵的行列式,你能在我的代码中找到错误吗?n 可以是任何大于 1 的自然数
- python - 如何将整数列转换为 [0,1] 的值范围?
- python - 使用 Boost Python Numpy ndarray 作为类成员变量
- c# - Visual Studio (Shell) 状态栏颜色和图标变化问题
- python - 如何正确激活 RunIfOutofBounds?
- reactjs - 我如何在 redux 中测试组件
- flutter - 我的 Timer 快疯了,它会减少 2 , 3 秒,而它应该只有 1 (每秒都会调用 Timer)
- python - 在全国范围内访问 postgresql 数据库
- postgresql - 如何从另一个未完成的行更新一行