Skip to content
话题
Seaborn
Seaborn 箱线图:如何在 Python 中创建和自定义箱线图

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() 中最常用参数的快速参考:

参数类型描述
dataDataFrame、数组或列表输入数据结构
xystr 或数组坐标轴的变量
huestr用于颜色编码子组的分组变量
orderstr 列表绘制分类级别的顺序
hue_orderstr 列表hue 级别的顺序
orient"v""h"图的方向
colorstr所有元素的单一颜色
palettestr、列表或字典不同级别的颜色
saturationfloat原始饱和度的比例(0 到 1)
fillbool是否用颜色填充箱体(seaborn >= 0.13)
widthfloat箱体的宽度(默认 0.8)
dodgebool是否沿分类轴移动 hue 组
fliersizefloat异常值标记的大小
linewidthfloat箱体边框线的宽度
whisfloat 或元组须长度作为 IQR 的倍数(默认 1.5)
axmatplotlib 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 pygwalker
import 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) 这样的工具可以让您构建和迭代可视化,而无需更改代码。

从上面的基本示例开始,尝试调色板和叠加层,您将拥有一个可靠的可视化工具包,用于解决任何数据分布问题。

📚