如何为分面分组条形图的sns.catplot添加自定义误差棒?
用预计算误差棒绘制Seaborn柱状图(两种实现方式)
环境依赖:pandas 1.5.3、seaborn 0.12.2
方法一:直接用Seaborn的errorbar参数(推荐)
Seaborn 0.12+版本支持在catplot(kind='bar')中通过errorbar参数传入自定义误差逻辑,刚好适配你预计算的val_lo(下限)和val_hi(上限)。只需定义一个lambda函数,返回每个数据点的上下误差范围即可。
修改后完整代码
import pandas as pd import re import seaborn as sns from matplotlib.ticker import PercentFormatter import matplotlib.pyplot as plt df = pd.DataFrame([ ['C', 'G1', 'gbt', 'auc', 0.7999, 0.7944, 0.8032], ['C', 'G1', 'gbtv2', 'auc', 0.8199, 0.8144, 0.8232], ['C', 'G1', 'gbt', 'pr@2%', 0.0883, 0.0841, 0.0909], ['C', 'G1', 'gbt', 'pr@10%', 0.0430, 0.0416, 0.0435], ['C', 'G2', 'gbt', 'auc', 0.7554, 0.7506, 0.7573], ['C', 'G2', 'gbt', 'pr@2%', 0.0842, 0.0795, 0.0872], ['C', 'G2', 'gbt', 'pr@10%', 0.0572, 0.0556, 0.0585], ['C', 'G3', 'gbt', 'auc', 0.7442, 0.7404, 0.7460], ['C', 'G3', 'gbt', 'pr@2%', 0.0894, 0.0836, 0.0913], ['C', 'G3', 'gbt', 'pr@10%', 0.0736, 0.0714, 0.0742], ['E', 'G1', 'gbt', 'auc', 0.7988, 0.7939, 0.8017], ['E', 'G1', 'gbt', 'pr@2%', 0.0810, 0.0770, 0.0832], ['E', 'G1', 'gbt', 'pr@10%', 0.0354, 0.0342, 0.0361], ['E', 'G1', 'gbtv3','pr@10%',0.0454, 0.0442, 0.0461], ['E', 'G2', 'gbt', 'auc', 0.7296, 0.7253, 0.7311], ['E', 'G2', 'gbt', 'pr@2%', 0.1071, 0.1034, 0.1083], ['E', 'G2', 'gbt', 'pr@10%', 0.0528, 0.0508, 0.0532], ['E', 'G3', 'gbt', 'auc', 0.6958, 0.6914, 0.6978], ['E', 'G3', 'gbt', 'pr@2%', 0.1007, 0.0961, 0.1030], ['E', 'G3', 'gbt', 'pr@10%', 0.0536, 0.0518, 0.0541], ], columns=["src","grp","model","metric","val","val_lo","val_hi"]) sns.reset_defaults() sns.set(style="whitegrid", font_scale=1.) # 核心修改:添加errorbar参数,传入自定义误差计算逻辑 g = sns.catplot(data=df, x="grp", y="val", hue="model", col="metric", row="src", kind="bar", sharey=False, errorbar=lambda x: (x['val'] - x['val_lo'], x['val_hi'] - x['val'])) for ax in g.axes.flat: ax.yaxis.set_major_formatter(PercentFormatter(1)) if re.search("metric = auc",ax.get_title(),re.IGNORECASE): _ = ax.set_ylim((.5,1.)) plt.show()
说明
lambda函数接收每个分组的子DataFrame,返回元组:第一个元素是下限误差(val - val_lo),第二个是上限误差(val_hi - val),Seaborn会自动将这些误差值对应到每个柱状图上。
方法二:手动用Matplotlib的errorbar添加误差棒
如果需要更灵活控制误差棒样式(如颜色、帽宽),可以先绘制基础柱状图,再遍历每个子轴手动添加误差棒。
修改后完整代码
import pandas as pd import re import seaborn as sns from matplotlib.ticker import PercentFormatter import matplotlib.pyplot as plt df = pd.DataFrame([ ['C', 'G1', 'gbt', 'auc', 0.7999, 0.7944, 0.8032], ['C', 'G1', 'gbtv2', 'auc', 0.8199, 0.8144, 0.8232], ['C', 'G1', 'gbt', 'pr@2%', 0.0883, 0.0841, 0.0909], ['C', 'G1', 'gbt', 'pr@10%', 0.0430, 0.0416, 0.0435], ['C', 'G2', 'gbt', 'auc', 0.7554, 0.7506, 0.7573], ['C', 'G2', 'gbt', 'pr@2%', 0.0842, 0.0795, 0.0872], ['C', 'G2', 'gbt', 'pr@10%', 0.0572, 0.0556, 0.0585], ['C', 'G3', 'gbt', 'auc', 0.7442, 0.7404, 0.7460], ['C', 'G3', 'gbt', 'pr@2%', 0.0894, 0.0836, 0.0913], ['C', 'G3', 'gbt', 'pr@10%', 0.0736, 0.0714, 0.0742], ['E', 'G1', 'gbt', 'auc', 0.7988, 0.7939, 0.8017], ['E', 'G1', 'gbt', 'pr@2%', 0.0810, 0.0770, 0.0832], ['E', 'G1', 'gbt', 'pr@10%', 0.0354, 0.0342, 0.0361], ['E', 'G1', 'gbtv3','pr@10%',0.0454, 0.0442, 0.0461], ['E', 'G2', 'gbt', 'auc', 0.7296, 0.7253, 0.7311], ['E', 'G2', 'gbt', 'pr@2%', 0.1071, 0.1034, 0.1083], ['E', 'G2', 'gbt', 'pr@10%', 0.0528, 0.0508, 0.0532], ['E', 'G3', 'gbt', 'auc', 0.6958, 0.6914, 0.6978], ['E', 'G3', 'gbt', 'pr@2%', 0.1007, 0.0961, 0.1030], ['E', 'G3', 'gbt', 'pr@10%', 0.0536, 0.0518, 0.0541], ], columns=["src","grp","model","metric","val","val_lo","val_hi"]) sns.reset_defaults() sns.set(style="whitegrid", font_scale=1.) # 先绘制基础柱状图,关闭默认误差棒 g = sns.catplot(data=df, x="grp", y="val", hue="model", col="metric", row="src", kind="bar", sharey=False, errorbar=None) # 遍历每个子图添加自定义误差棒 for ax in g.axes.flat: # 获取当前子图对应的src和metric row_val = g.row_names[ax.get_subplotspec().rowspan.start] col_val = g.col_names[ax.get_subplotspec().colspan.start] subset = df[(df['src'] == row_val) & (df['metric'] == col_val)] # 按grp和model排序,确保和bar的绘制顺序一致 subset_sorted = subset.sort_values(by=['grp', 'model']) bars = ax.patches # 逐个匹配bar和误差值 for bar, (_, row) in zip(bars, subset_sorted.iterrows()): x_center = bar.get_x() + bar.get_width() / 2 # bar的中心x坐标 y_val = row['val'] # 上下误差需用嵌套列表格式 y_err = [[row['val'] - row['val_lo']], [row['val_hi'] - row['val']]] ax.errorbar(x_center, y_val, yerr=y_err, fmt='none', c='black', capsize=3) # 保留原有的格式设置 ax.yaxis.set_major_formatter(PercentFormatter(1)) if re.search("metric = auc", ax.get_title(), re.IGNORECASE): ax.set_ylim((.5,1.)) plt.show()
说明
通过ax.patches获取每个柱状图的位置信息,再匹配对应数据行的误差值,调用ax.errorbar添加误差棒。可以通过修改c(颜色)、capsize(帽宽)等参数自定义样式。
内容的提问来源于stack exchange,提问作者ironv
相关产品推荐
相关产品推荐

