Seaborn 中如何让散点与窄箱线图对齐?
数据小许
结论
jitter 只调整散点在各自位置附近的横向分散范围,不能缩小 stripplot 中 dodge 造成的分组偏移。这里虽然调小了 boxplot 的 width,但 stripplot 的分组位置没有随之收窄,因此温度图中的两组散点会偏离箱体。
保留 sns.boxplot,再用 matplotlib 的 scatter 手动绘制散点即可。根据箱线图的类别顺序计算横坐标,并按箱线图宽度设置 hue 分组偏移和 jitter。
手动计算散点位置
下面的代码可以替换原来的绘图循环。类别位置由 order 确定,每组散点的中心与对应箱体对齐。jitter 按箱体宽度缩放,调整 width 时,散点的分布范围也会随之变化。
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
palette = ['r', 'g']
hue_order = ['exp', 'sim']
for c_category in ['temp', 'design']:
width = custom_width_box[c_category][0]
order = sorted(frame_scatter[c_category].dropna().unique())
fig, ax = plt.subplots(figsize=(6.4, 4.8), dpi=400)
sns.boxplot(
data=frame_scatter,
x=c_category,
y='Measured_Value',
hue='Index_Name',
order=order,
hue_order=hue_order,
showfliers=False,
palette=palette,
boxprops={'alpha': 0.4},
width=width,
ax=ax,
)
hue_count = len(hue_order)
box_width = width / hue_count
jitter = box_width * 0.3
for hue_index, hue_level in enumerate(hue_order):
points = frame_scatter[frame_scatter.index == hue_level]
category_positions = pd.Categorical(
points[c_category], categories=order, ordered=True
).codes
# 每个 hue 组的箱体中心偏移
hue_offset = (hue_index - (hue_count - 1) / 2) * box_width
x = category_positions + hue_offset + np.random.uniform(
-jitter, jitter, size=len(points)
)
ax.scatter(
x,
points['Measured_Value'],
color=palette[hue_index],
s=18,
zorder=3,
)
ax.grid(True)
plt.show()
调整参数
width控制箱线图在类别位置附近占用的总宽度。box_width = width / hue_count用来计算每个 hue 组对应的宽度。hue_offset将各组散点的中心移到对应箱体中心。jitter控制散点在各自箱体中心附近的分散范围。想让散点更紧凑,可以调小0.3;想让分布更宽,可以适当调大,但建议不超过0.5,以免散点明显超出箱体。
示例假定 frame_scatter 的索引值为 exp 和 sim,且 palette 的顺序与 hue_order 一致。如果实际类别顺序不同,相应调整这两个列表。
备注:内容仅供参考。
