3.3 relplot 分面工厂 relplot 是关系家族的 figure 级工厂:kind 在 scatter 与 line 之间切换,col 与 row 把数据切片铺成面板网格,返回 FacetGrid 对象供继续打磨。 3.1 与 3.2 的 axes 级函数一次只画一个坐标轴;当关系问题里出现"分性别看"、"分地区看"的诉求,就该 relplot 登场。它也是第 2.3 节 displot 的姊妹篇——两者共享同一套分面语法,对照着学一次记两族。 kind 与分面通道的组合矩阵 语法层面只有三件事要记:kind 决定图层(scatter 或 line,默认 scatter);col、row 是分面通道,各自吃一个类别列;
relplot 是关系家族的 figure 级工厂:kind 在 scatter 与 line 之间切换,col 与 row 把数据切片铺成面板网格,返回 FacetGrid 对象供继续打磨。
3.1 与 3.2 的 axes 级函数一次只画一个坐标轴;当关系问题里出现"分性别看"、"分地区看"的诉求,就该 relplot 登场。它也是第 2.3 节 displot 的姊妹篇——两者共享同一套分面语法,对照着学一次记两族。
import seaborn as sns tips = sns.load_dataset('tips') # 组合一:散点 + 单分面 g = sns.relplot( data=tips, x='total_bill', y='tip', col='time', # 午餐晚餐各一个面板 kind='scatter', hue='smoker', height=3.5, ) # 输出说明:Dinner 面板的散点带更长更宽,Lunch 面板集中在低消费区; # 两个面板 x 轴刻度自动共享,跨面板比较不需要读者自己对齐 # 组合二:折线 + 双分面 g = sns.relplot( data=tips, x='size', y='tip_rate', col='time', row='smoker', kind='line', height=2.8, ) # 输出说明:2 x 2 网格里,size=1 的置信带很宽(样本极少), # 提示这张图更适合当作假设生成器而不是结论依据
语法层面只有三件事要记:kind 决定图层(scatter 或 line,默认 scatter);col、row 是分面通道,各自吃一个类别列;height 与 aspect 控制单个面板的尺寸,整张图尺寸是两者乘上网格规模。axes 级函数的一切映射参数(hue、size、style)原样照搬——这是"映射参数跨级通用"的又一次兑现。
figure 级函数返回的 Grid 对象自带一套打磨接口,这是它与 axes 级函数最大的使用差异:
g = sns.relplot( data=tips, x='total_bill', y='tip', col='day', col_wrap=2, kind='scatter', height=3.2, ) g.set_axis_labels('总消费(美元)', '小费(美元)') # 批量改两个轴标签 g.set_titles('{col_name} 的账单') # 面板标题模板,col_name 是占位符 g.tight_layout() # 防止标签被裁切 # 输出说明:四个面板标题从默认的 day=Sun 变成自然语言; # col_wrap=2 让周四到周日排成两行,而不是四个瘦长条
需要逐面板定制时,Grid 对象可以当二维数组迭代:
# 给每个面板加一条组内均值参考线 for day, ax in g.axes_dict.items(): sub = tips[tips['day'] == day] ax.axhline(sub['tip'].mean(), color='gray', linestyle=':', linewidth=1)
axes_dict 以分面取值为键、以 Axes 为值,是逐面板加工的标准入口。第 7 章会把这套 Grid 机制彻底拆开。
背景:传说吸烟者给小费更慷慨,运营想验证,并看这个效应是否随餐段变化。
操作:
tips['tip_rate'] = tips['tip'] / tips['total_bill'] g = sns.relplot( data=tips, x='total_bill', y='tip_rate', col='time', hue='smoker', style='smoker', kind='scatter', height=3.6, alpha=0.7, ) g.set_axis_labels('总消费(美元)', '小费比例')
结果解读:横向读图,高消费区(40 美元以上)几乎只剩吸烟桌的点了——吸烟客群消费上限更高;但纵向看小费比例,两组的点云互相渗透,比例差异远不如传说中明显。更细的地方在 Dinner 面板:非吸烟桌的点云在高消费区收得更紧,说明大额账单上非吸烟者的小费比例更稳定。"吸烟者更慷慨"被修正为"吸烟者出现在极端账单里的频率更高",一字之差,结论方向不同。
变式:把 kind='line'、x 换成 size,得到"随人数增长的小费比例曲线",可以直接读出多人桌是否按比例缩减小费。
| 对比项 | displot(第 2 章) | relplot(本章) |
|---|---|---|
| kind 取值 | hist / kde / ecdf | scatter / line |
| 必需映射 | 一个数值列 x | 两个数值列 x 与 y |
| 分面通道 | col、row、col_wrap | 完全一致 |
| 返回对象 | FacetGrid | FacetGrid |
| 打磨接口 | set_axis_labels、set_titles | 完全一致 |
💡 关键直觉:figure 级函数学到第二个,分面语法就已经学完了——catplot(第 4 章)沿用同一套 col、row、height、aspect。Seaborn 的 API 复利就在这里。
本节要点回顾
- kind 是图层开关:scatter 与 line 之外没有第三个关系图层;
- col_wrap 管排版:面板多于 4 个时几乎总要设它;
- set_titles 用模板:col_name 占位符把面板标题从机器味改成自然语言;
- axes_dict 逐面板加工:参考线、注释、局部缩放都从它进;
- 置信带宽度即证据强度:宽带面板的结论要降级处理。
面板数量决定一切排版决策:四以内单行排完,五到九用 col_wrap 两到三行,十以上考虑这份数据是否真的需要分面(也许按组聚合更诚实)。height 与 aspect 的搭配经验值是 height 乘 aspect 接近 4 到 5(如 height=3、aspect=1.4),单面板太窄时折线会显得抖动加剧——这是视觉伪影不是数据变化。批量出图前先画一个"最挤的面板"检查极限情况,比分摊风险更省事。
关系家族拆解完毕。第 4 章把 x 轴换成类别,八种分类图将展示同一语法在组间比较上的变奏。