Seaborn 箱线图:如何在 Python 中创建和自定义箱线图
Updated on
了解数据分布是数据分析中最基本的任务之一。您需要发现异常值、比较组别,并在运行任何模型或得出结论之前识别偏态。然而,盯着 DataFrame 中的原始数字很难看到全貌。seaborn 箱线图通过将整个分布浓缩为紧凑、可读的图形来解决这个问题,它能一目了然地显示中位数、分散度和异常值。
在本指南中,您将学习如何使用 Python 的 seaborn 库创建、自定义和解释箱线图。每个代码示例都使用 seaborn 内置的真实数据集,因此您可以立即在 Jupyter notebook 中运行它们。
什么是箱线图?
箱线图(也称为箱须图)是基于五个汇总统计量显示分布的标准化方法:
| 组成部分 | 代表的含义 |
|---|---|
| 中位线 | 数据集的中间值(第 50 百分位数) |
| 箱体(IQR) | 四分位距,从 Q1(第 25 百分位数)到 Q3(第 75 百分位数) |
| 下须 | Q1 - 1.5 * IQR 范围内的最小数据点 |
| 上须 | Q3 + 1.5 * IQR 范围内的最大数据点 |
| 异常值点 | 落在须范围之外的单个数据点 |
箱体捕获数据的中间 50%。高箱体意味着高变异性;短箱体意味着值聚集紧密。当中位线在箱体内偏离中心时,分布是偏态的。须之外的点标记出值得调查的潜在异常值。
当您需要并排比较多个类别的分布时,箱线图特别有效,使其成为探索性数据分析的主要工具。
基本 Seaborn 箱线图语法
在 seaborn 中创建箱线图只需要一个函数:sns.boxplot()。至少,您需要传递数据并指定要绘制的变量。
import seaborn as sns
import matplotlib.pyplot as plt
# 加载内置数据集
tips = sns.load_dataset("tips")
# 基本垂直箱线图
sns.boxplot(data=tips, y="total_bill")
plt.title("总账单分布")
plt.show()这会生成一个显示 total_bill 值分布的单个箱线图。该函数会自动为您计算四分位数、须和异常值。
要按分类变量拆分数据,请添加 x 参数:
sns.boxplot(data=tips, x="day", y="total_bill")
plt.title("一周各天的总账单")
plt.show()现在您会看到四个并排的箱线图,每天一个,这使得比较一周的消费模式变得容易。
从不同数据格式创建箱线图
Seaborn 优雅地处理多种数据格式。以下是最常见的场景。
长格式 DataFrame(整洁数据)
大多数 seaborn 函数最适合长格式(整洁)数据,其中每行是一次观察,列代表变量。tips 数据集已经是这种格式:
# 长格式:每行是一次餐厅访问
sns.boxplot(data=tips, x="day", y="total_bill")
plt.show()宽格式 DataFrame
如果您的数据每组一列(宽格式),seaborn 仍然可以直接生成箱线图:
import pandas as pd
import numpy as np
# 创建宽格式数据
np.random.seed(42)
wide_df = pd.DataFrame({
"Group A": np.random.normal(50, 10, 100),
"Group B": np.random.normal(60, 15, 100),
"Group C": np.random.normal(45, 8, 100),
})
sns.boxplot(data=wide_df)
plt.title("比较三组(宽格式数据)")
plt.ylabel("值")
plt.show()Seaborn 自动将每列视为单独的类别并并排绘制它们。
选择特定 DataFrame 列
当您只想可视化较大 DataFrame 中的某些数值列时,首先过滤它们:
iris = sns.load_dataset("iris")
# 仅选择测量列
measurement_cols = iris[["sepal_length", "sepal_width", "petal_length", "petal_width"]]
sns.boxplot(data=measurement_cols)
plt.title("鸢尾花特征分布")
plt.xticks(rotation=15)
plt.show()自定义您的 Seaborn 箱线图
Seaborn 提供了广泛的选项来定制箱线图的外观和行为。
颜色和调色板
使用 palette 参数更改配色方案,或为所有箱线图设置单一颜色:
# 使用命名调色板
sns.boxplot(data=tips, x="day", y="total_bill", palette="Set2")
plt.title("自定义调色板")
plt.show()# 所有箱线图使用单一颜色
sns.boxplot(data=tips, x="day", y="total_bill", color="skyblue")
plt.title("统一颜色")
plt.show()流行的调色板选项包括 "Set2"、"pastel"、"muted"、"deep"、"husl" 和 "coolwarm"。
水平与垂直方向
交换坐标轴以创建水平箱线图。当类别标签很长时,这很有用:
sns.boxplot(data=tips, x="total_bill", y="day", orient="h")
plt.title("水平箱线图")
plt.show()使用 hue 参数的分组箱线图
hue 参数将每个类别拆分为子组,为您的比较添加第二个维度:
sns.boxplot(data=tips, x="day", y="total_bill", hue="sex")
plt.title("按天和性别的总账单")
plt.legend(title="性别")
plt.show()现在每天显示两个箱线图(每个性别一个),使得在一周的每一天比较男性与女性的消费模式变得简单。
控制图形大小
Seaborn 图继承 matplotlib 的图形大小。在调用 sns.boxplot() 之前设置它:
plt.figure(figsize=(12, 6))
sns.boxplot(data=tips, x="day", y="total_bill", hue="smoker", palette="muted")
plt.title("按天和吸烟状态的总账单")
plt.show()添加散点图或带状图叠加层
箱线图总结了分布,但它隐藏了单个数据点。叠加散点图或带状图以显示每次观察:
plt.figure(figsize=(10, 6))
sns.boxplot(data=tips, x="day", y="total_bill", palette="pastel")
sns.stripplot(data=tips, x="day", y="total_bill", color="0.3", size=3, jitter=True, alpha=0.5)
plt.title("带有带状图叠加的箱线图")
plt.show()plt.figure(figsize=(10, 6))
sns.boxplot(data=tips, x="day", y="total_bill", palette="pastel")
sns.swarmplot(data=tips, x="day", y="total_bill", color="0.25", size=3, alpha=0.6)
plt.title("带有散点图叠加的箱线图")
plt.show()散点图排列点以使它们不重叠,更好地展示数据密度。当您有许多数据点时使用带状图,当您的数据点较少时使用散点图(散点图在处理数千个点时可能会变慢)。
Seaborn 箱线图参数参考
以下是 sns.boxplot() 中最常用参数的快速参考:
| 参数 | 类型 | 描述 |
|---|---|---|
data | DataFrame、数组或列表 | 输入数据结构 |
x、y | str 或数组 | 坐标轴的变量 |
hue | str | 用于颜色编码子组的分组变量 |
order | str 列表 | 绘制分类级别的顺序 |
hue_order | str 列表 | hue 级别的顺序 |
orient | "v" 或 "h" | 图的方向 |
color | str | 所有元素的单一颜色 |
palette | str、列表或字典 | 不同级别的颜色 |
saturation | float | 原始饱和度的比例(0 到 1) |
fill | bool | 是否用颜色填充箱体(seaborn >= 0.13) |
width | float | 箱体的宽度(默认 0.8) |
dodge | bool | 是否沿分类轴移动 hue 组 |
fliersize | float | 异常值标记的大小 |
linewidth | float | 箱体边框线的宽度 |
whis | float 或元组 | 须长度作为 IQR 的倍数(默认 1.5) |
ax | matplotlib Axes | 要在其上绘制图的 Axes 对象 |
比较分布
并排多列
当比较多个数值特征的分布时,将您的 DataFrame 转换为长格式:
iris = sns.load_dataset("iris")
# 从宽格式转换为长格式
iris_long = iris.melt(id_vars="species", var_name="measurement", value_name="cm")
plt.figure(figsize=(12, 6))
sns.boxplot(data=iris_long, x="measurement", y="cm", palette="Set3")
plt.title("鸢尾花测量值比较")
plt.show()使用 hue 的分组比较
结合类别和分组变量来比较两个维度的分布:
plt.figure(figsize=(14, 6))
sns.boxplot(data=iris_long, x="measurement", y="cm", hue="species", palette="husl")
plt.title("按品种的鸢尾花测量值")
plt.legend(title="品种", bbox_to_anchor=(1.05, 1), loc="upper left")
plt.tight_layout()
plt.show()这会生成一个分组箱线图,其中每种测量类型显示三个箱线图(每个品种一个),使跨品种比较变得即时。
Seaborn 箱线图 vs Matplotlib 箱线图
seaborn 和 matplotlib 都可以生成箱线图,但它们在易用性和视觉质量方面有显著差异。
| 特征 | Seaborn sns.boxplot() | Matplotlib ax.boxplot() |
|---|---|---|
| 默认美学 | 精致、可发表 | 基本、最小样式 |
| DataFrame 集成 | 原生支持 pandas DataFrame | 需要手动提取数组 |
| Hue 分组 | 内置 hue 参数 | 手动定位和着色 |
| 调色板 | 单行调色板分配 | 手动颜色列表管理 |
| 统计注释 | 轻松叠加散点图/带状图 | 需要手动散点叠加 |
| 自定义深度 | 中等(委托给 matplotlib) | 完全的低级控制 |
| 学习曲线 | 低 | 中到高 |
| 代码冗长度 | 分组图 1-2 行 | 等效的 10-20 行 |
结论:使用 seaborn 进行快速探索性分析和干净的视觉效果。当您需要对每个元素进行像素级控制时,回退到 matplotlib。
# Matplotlib 箱线图(更冗长)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 5))
data_by_day = [tips[tips["day"] == d]["total_bill"].values for d in ["Thur", "Fri", "Sat", "Sun"]]
bp = ax.boxplot(data_by_day, labels=["Thur", "Fri", "Sat", "Sun"], patch_artist=True)
for patch, color in zip(bp["boxes"], ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072"]):
patch.set_facecolor(color)
ax.set_title("Matplotlib 箱线图(手动样式)")
ax.set_ylabel("总账单")
plt.show()# Seaborn 等效(简洁)
sns.boxplot(data=tips, x="day", y="total_bill", palette="Set3",
order=["Thur", "Fri", "Sat", "Sun"])
plt.title("Seaborn 箱线图(一行)")
plt.show()箱线图 vs 小提琴图:何时使用哪个
Seaborn 还提供 sns.violinplot(),它显示分布的完整密度形状,而不仅仅是汇总统计量。以下是选择每个的时机:
| 标准 | 箱线图 | 小提琴图 |
|---|---|---|
| 最适合 | 快速汇总统计和异常值检测 | 理解分布的完整形状 |
| 显示异常值 | 是,作为单个点 | 否(被吸收到密度曲线中) |
| 显示双峰性 | 否 | 是(显示为两个凸起) |
| 可读性 | 高,即使对非技术受众 | 需要更多解释 |
| 空间效率 | 紧凑 | 更宽,占用更多水平空间 |
| 性能 | 渲染快 | 大数据集较慢(KDE 计算) |
经验法则:从箱线图开始进行快速检查。如果您怀疑分布有多个峰值或不寻常的形状,切换到小提琴图。
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
sns.boxplot(data=tips, x="day", y="total_bill", palette="Set2", ax=axes[0])
axes[0].set_title("箱线图")
sns.violinplot(data=tips, x="day", y="total_bill", palette="Set2", ax=axes[1])
axes[1].set_title("小提琴图")
plt.tight_layout()
plt.show()交互式替代方案:使用 PyGWalker 探索分布
静态箱线图适用于报告和笔记本,但当您处于早期探索阶段时,您通常希望将不同的变量拖入和拖出图表,而无需每次重写代码。
PyGWalker (opens in a new tab) 是一个开源 Python 库,可以直接在 Jupyter Notebook 中将任何 pandas DataFrame 转换为交互式的类 Tableau 可视化探索界面。您只需将字段拖到坐标轴上即可创建箱线图、小提琴图、直方图、散点图等。无需更改代码。
pip install pygwalkerimport pandas as pd
import pygwalker as pyg
tips = pd.read_csv("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv")
walker = pyg.walk(tips)UI 加载后,将 day 拖到 X 轴,将 total_bill 拖到 Y 轴,然后选择箱线图标记类型。您可以立即切换到小提琴图、添加颜色维度或按任何列过滤,所有这些都无需编写一行额外的代码。
这对于并非所有人都编写 Python 的团队特别有价值。共享笔记本,让利益相关者自己探索数据。
| 在 Kaggle 中运行 PyGWalker (opens in a new tab) | 在 Google Colab 中运行 PyGWalker (opens in a new tab) | GitHub 上的 PyGWalker (opens in a new tab) |
|---|
常见问题
如何从 seaborn 箱线图中删除异常值?
将 whis 参数设置为较大的值(例如 whis=3.0)以延长须并减少显示为异常值的点数。或者,设置 flierprops={"marker": ""} 以完全隐藏异常值标记,同时保持须计算不变。
我可以在不使用 DataFrame 的情况下绘制 seaborn 箱线图吗?
可以。您可以直接传递原始数组或列表:sns.boxplot(x=["A"]*50 + ["B"]*50, y=np.random.randn(100))。但是,建议使用带命名列的 DataFrame,因为它会自动生成坐标轴标签。
如何更改 sns.boxplot 中的异常值标记样式?
向 flierprops 参数传递一个字典:sns.boxplot(data=tips, x="day", y="total_bill", flierprops={"marker": "D", "markerfacecolor": "red", "markersize": 5})。这会将异常值标记更改为红色菱形。
seaborn 箱线图中 hue 和 x 有什么区别?
x 参数定义水平轴上的主要分类分组。hue 参数在每个类别内添加次要分组,显示为并排的彩色箱线图。单独使用 x 进行简单比较,当您想按性别或状态等第二个变量分解每个类别时添加 hue。
如何将 seaborn 箱线图保存到文件?
创建图后,调用 plt.savefig("boxplot.png", dpi=300, bbox_inches="tight")。Seaborn 图在底层是 matplotlib 图形,因此所有 matplotlib 保存方法都有效。支持的格式包括 PNG、PDF、SVG 和 EPS。
结论
seaborn 箱线图是理解数据分布、发现异常值和比较组别的最快方法之一。使用 sns.boxplot(),您可以用一行代码从原始 DataFrame 生成可发表质量的可视化。hue 参数可以轻松添加分组比较,叠加带状图或散点图可以填充箱线图抽象掉的单个数据点细节。
对于笔记本和报告中的静态分析,seaborn 箱线图难以超越。当您需要在 pandas 数据之上的交互式探索层时,像 PyGWalker (opens in a new tab) 这样的工具可以让您构建和迭代可视化,而无需更改代码。
从上面的基本示例开始,尝试调色板和叠加层,您将拥有一个可靠的可视化工具包,用于解决任何数据分布问题。