6.3 标注图例与坐标细节 成图交付的最后一公里:轴标签与标题的接口分层(set 系列、setaxislabels、settitles)、图例的搬移与精简(movelegend、legend=False)、坐标范围与参考线(setxlim、axhline)——这些细节决定读者是否第一眼信任这张图。 6.2 结束时成图已经"风格正确",本节处理"信息完整"。原教程把这批内容拆成两节(标注加图例、网格加刻度),此处合并为一个交付检查清单,因为它们本来就是同一个动作:交图前的最后一轮打磨。 标注接口的分层 两套接口的分工与第 1.2 节的两级函数完全对应:Axes 的 set 管单图,Grid 的 set 系列管面板阵列。figure 级总标题用 suptitle 而非 plt.
成图交付的最后一公里:轴标签与标题的接口分层(set 系列、set_axis_labels、set_titles)、图例的搬移与精简(move_legend、legend=False)、坐标范围与参考线(set_xlim、axhline)——这些细节决定读者是否第一眼信任这张图。
6.2 结束时成图已经"风格正确",本节处理"信息完整"。原教程把这批内容拆成两节(标注加图例、网格加刻度),此处合并为一个交付检查清单,因为它们本来就是同一个动作:交图前的最后一轮打磨。
import seaborn as sns import matplotlib.pyplot as plt tips = sns.load_dataset('tips') # axes 级:Axes 的 set 系列,一次全改 ax = sns.scatterplot(data=tips, x='total_bill', y='tip', hue='day') ax.set( xlabel='总消费(美元)', ylabel='小费(美元)', title='小费随总消费的增长', xlim=(0, 60), ) # 等价于分别调 set_xlabel、set_ylabel、set_title、set_xlim,一个调用打包 # figure 级:Grid 的批量接口 g = sns.relplot(data=tips, x='total_bill', y='tip', col='time', height=3.5) g.set_axis_labels('总消费(美元)', '小费(美元)') # 只写一次,所有面板共享 g.set_titles('{col_name}时段') # 模板化面板标题 g.fig.suptitle('小费行为的餐段差异', y=1.02) # 总标题挂在 fig 上 plt.show()
两套接口的分工与第 1.2 节的两级函数完全对应:Axes 的 set 管单图,Grid 的 set 系列管面板阵列。figure 级总标题用 suptitle 而非 plt.title,是新手最常踩的接口错位。
# 处置一:搬移——move_legend 处理最常见的"图例挡数据" ax = sns.scatterplot(data=tips, x='total_bill', y='tip', hue='day') sns.move_legend(ax, 'upper left', title='星期', frameon=False) # loc 支持九宫格位置;frameon=False 去掉图例边框,更轻 # 处置二:精简——单图不需要图例时直接关掉 ax = sns.scatterplot(data=tips, x='total_bill', y='tip', hue='day', legend=False) # 单面板且类别含义已在标题说清时 # 处置三:外包——多个子图共享一个图例 fig, axes = plt.subplots(1, 2, figsize=(10, 4)) sns.scatterplot(data=tips, x='total_bill', y='tip', hue='day', ax=axes[0], legend=False) sns.scatterplot(data=tips, x='size', y='tip_rate', hue='day', ax=axes[1], legend=False) handles, labels = axes[0].get_legend_handles_labels() fig.legend(handles, labels, loc='lower center', ncol=4, title='星期') fig.subplots_adjust(bottom=0.18) # 给底部图例腾位置
图例位置的原则只有一条:不挡数据。数据集中在左上时图例放右下,反之亦然;实在没有空位就把图例移出绘图区(外包到 fig 层)。
# 刻度定制:rotation 解救长标签 ax = sns.countplot(data=tips, x='day') ax.set_xticklabels(['周四', '周五', '周六', '周日']) ax.tick_params(axis='x', rotation=30) # 参考线:把领域知识钉进坐标系 ax = sns.histplot(data=tips, x='total_bill', binwidth=5) ax.axvline(20, color='gray', linestyle='--', linewidth=1) ax.annotate('套餐定价线 20 美元', xy=(20, 40), xytext=(26, 45), arrowprops={'arrowstyle': '->', 'color': 'gray'}) # 对数轴:金额跨度大时 ax = sns.histplot(data=tips, x='total_bill') ax.set_xscale('log') # 消费金额右偏严重时,对数轴还原对称形态

背景:分析图已出结论(小费比例随账单增大而收敛),但当前版本图例挡数据、无单位、标题是图型名,直接交出去会被打回。
操作(按清单逐项执行):
tips['tip_rate'] = tips['tip'] / tips['total_bill'] ax = sns.scatterplot(data=tips, x='total_bill', y='tip_rate', hue='day', style='time', alpha=0.7) # 一:标签与结论式标题 ax.set_xlabel('总消费(美元)') ax.set_ylabel('小费占账单比例') ax.set_title('小费比例随账单增大而收敛于两成附近') # 二:图例搬移 sns.move_legend(ax, 'upper right', title='星期') # 三:参考线 ax.axhline(0.2, color='crimson', linestyle=':', linewidth=1.2) ax.annotate('两成小费线', xy=(45, 0.2), xytext=(46, 0.28), arrowprops={'arrowstyle': '->', 'color': 'crimson'}, color='crimson') # 四:范围 ax.set_ylim(0, 0.75) # 五:收尾 sns.despine(ax=ax, offset=6)
结果解读:打磨后的图自含完整叙事——标题给结论、参考线给基准、散点的收敛形态给证据、图例无遮挡。同一个 axes,五步只动了细节层,统计内容零改动。变式:交付幻灯版再叠一层 6.2 的 context(talk),字号自动放大,叙事结构不变。
⚠️ 常见坑:
set_xticklabels必须与当前刻度数一致,否则报错;它改的只是文字,类别顺序由绘图时的顺序决定。想改顺序应在数据层把列转成有序 category,而不是事后换标签。
本节要点回顾
- 接口分层跟两级函数走:Axes.set 管单图、Grid.set 系列管面板、总标题挂 fig;
- 图例唯一原则是不挡数据:move_legend、legend=False、fig 外包共享三选一;
- 结论式标题:标题说发现,不说图型名;
- 领域知识进图:参考线加注释把基准与阈值钉进坐标系;
- 顺序问题回数据层:刻度顺序、类别顺序都在数据层解决,标注层只管文字。
细节层至此清算完毕。第 7 章进入多面板装配与生态集成,把单图零件组装成成套产品。