Matplotlib 分组箱线图绘制指南:如何使用 boxplot 展示分组数据
参考:matplotlib boxplot by group
Matplotlib 是 Python 中最流行的数据可视化库之一,它提供了丰富的绘图功能,其中箱线图(boxplot)是一种非常有用的统计图表。本文将详细介绍如何使用 Matplotlib 绘制分组箱线图,帮助您更好地理解和展示分组数据的分布情况。
1. 箱线图简介
箱线图,也称为盒须图,是一种用于显示一组数据分布情况的统计图表。它可以直观地展示数据的中位数、四分位数、异常值等统计信息。在数据分析中,箱线图常用于比较不同组别或类别的数据分布。
以下是一个简单的箱线图示例:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data = np.random.randn(100)
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制箱线图
ax.boxplot(data)
# 设置标题和标签
ax.set_title('Simple Boxplot - how2matplotlib.com')
ax.set_xlabel('Group')
ax.set_ylabel('Value')
# 显示图形
plt.show()
Output:
这个示例展示了如何绘制一个基本的箱线图。我们使用 numpy
生成随机数据,然后通过 boxplot()
函数绘制箱线图。图中的箱体表示数据的四分位范围,中间的线表示中位数,须线表示数据的范围,而点则表示异常值。
2. 分组箱线图的基本概念
分组箱线图是将多组数据的箱线图绘制在同一个坐标系中,以便直观地比较不同组别的数据分布。这种图表特别适合用于比较多个类别或组别的数据特征。
下面是一个简单的分组箱线图示例:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
group1 = np.random.normal(0, 1, 100)
group2 = np.random.normal(2, 1.5, 100)
group3 = np.random.normal(-1, 2, 100)
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制分组箱线图
ax.boxplot([group1, group2, group3], labels=['Group 1', 'Group 2', 'Group 3'])
# 设置标题和标签
ax.set_title('Grouped Boxplot - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示图形
plt.show()
Output:
在这个例子中,我们创建了三组具有不同均值和标准差的正态分布数据,然后使用 boxplot()
函数将它们绘制在同一个图表中。这样我们可以直观地比较三组数据的分布情况。
3. 数据准备和格式化
在绘制分组箱线图之前,我们需要正确地准备和格式化数据。通常,数据可以以列表的列表、字典或 pandas DataFrame 的形式组织。
3.1 使用列表的列表
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制箱线图
ax.boxplot(data)
# 设置标题和标签
ax.set_title('Boxplot with List of Lists - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示图形
plt.show()
Output:
在这个例子中,我们使用列表推导式创建了一个包含三个子列表的列表,每个子列表代表一组数据。
3.2 使用字典
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = {f'Group {i}': np.random.normal(i, 1, 100) for i in range(1, 4)}
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制箱线图
ax.boxplot(data.values())
# 设置标题和标签
ax.set_title('Boxplot with Dictionary - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
ax.set_xticklabels(data.keys())
# 显示图形
plt.show()
Output:
这个例子展示了如何使用字典来组织数据。字典的键作为组名,值作为每组的数据。
3.3 使用 pandas DataFrame
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# 创建 DataFrame
df = pd.DataFrame({
'Group A': np.random.normal(0, 1, 100),
'Group B': np.random.normal(2, 1.5, 100),
'Group C': np.random.normal(-1, 2, 100)
})
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制箱线图
df.boxplot(ax=ax)
# 设置标题和标签
ax.set_title('Boxplot with pandas DataFrame - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示图形
plt.show()
Output:
使用 pandas DataFrame 是处理和可视化分组数据的一种非常方便的方式。每列代表一个组,列名自动成为 x 轴的标签。
4. 自定义箱线图样式
Matplotlib 提供了多种方式来自定义箱线图的样式,使其更加美观和信息丰富。
4.1 修改箱体颜色和透明度
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制自定义样式的箱线图
bplot = ax.boxplot(data, patch_artist=True)
# 设置箱体颜色和透明度
colors = ['lightblue', 'lightgreen', 'lightpink']
for patch, color in zip(bplot['boxes'], colors):
patch.set_facecolor(color)
patch.set_alpha(0.7)
# 设置标题和标签
ax.set_title('Customized Boxplot - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示图形
plt.show()
Output:
在这个例子中,我们使用 patch_artist=True
参数来允许自定义箱体的颜色。然后,我们通过设置 facecolor
和 alpha
属性来修改箱体的颜色和透明度。
4.2 修改线条样式
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制自定义线条样式的箱线图
bplot = ax.boxplot(data, linestyle='--', linewidth=2)
# 设置中位数线的样式
for median in bplot['medians']:
median.set_color('red')
median.set_linewidth(3)
# 设置标题和标签
ax.set_title('Boxplot with Custom Line Styles - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示图形
plt.show()
这个例子展示了如何修改箱线图的线条样式。我们使用虚线样式和加粗的线宽,并特别强调了中位数线的颜色和宽度。
4.3 添加数据点
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = [np.random.normal(0, std, 30) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制箱线图并添加数据点
bplot = ax.boxplot(data, patch_artist=True, showfliers=False)
for i, d in enumerate(data):
y = d
x = np.random.normal(i+1, 0.04, len(y))
ax.scatter(x, y, alpha=0.5)
# 设置标题和标签
ax.set_title('Boxplot with Data Points - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示图形
plt.show()
Output:
这个例子展示了如何在箱线图上添加原始数据点。我们使用 scatter()
函数在每个箱体旁边绘制散点图,以显示数据的分布情况。
5. 处理分类数据
有时,我们需要根据分类变量来绘制箱线图。Matplotlib 可以很好地处理这种情况。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 创建示例数据
np.random.seed(42)
df = pd.DataFrame({
'group': np.repeat(['A', 'B', 'C'], 100),
'value': np.concatenate([
np.random.normal(0, 1, 100),
np.random.normal(2, 1.5, 100),
np.random.normal(-1, 2, 100)
])
})
# 创建图形和坐标轴
fig, ax = plt.subplots()
# 绘制箱线图
ax.boxplot([df[df['group'] == g]['value'] for g in ['A', 'B', 'C']])
# 设置标题和标签
ax.set_title('Boxplot for Categorical Data - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
ax.set_xticklabels(['A', 'B', 'C'])
# 显示图形
plt.show()
Output:
在这个例子中,我们创建了一个包含分类变量 ‘group’ 和数值变量 ‘value’ 的 DataFrame。然后,我们根据分类变量对数据进行分组,并为每个组绘制箱线图。
6. 多变量箱线图
当我们需要比较多个变量在不同组别间的分布时,多变量箱线图非常有用。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 创建示例数据
np.random.seed(42)
df = pd.DataFrame({
'group': np.repeat(['A', 'B', 'C'], 100),
'var1': np.random.normal(0, 1, 300),
'var2': np.random.normal(2, 1.5, 300),
'var3': np.random.normal(-1, 2, 300)
})
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制多变量箱线图
positions = [1, 2, 3, 5, 6, 7, 9, 10, 11]
variables = ['var1', 'var2', 'var3']
colors = ['lightblue', 'lightgreen', 'lightpink']
for i, var in enumerate(variables):
bp = ax.boxplot([df[df['group'] == g][var] for g in ['A', 'B', 'C']],
positions=positions[i*3:(i+1)*3],
patch_artist=True)
for patch in bp['boxes']:
patch.set_facecolor(colors[i])
# 设置标题和标签
ax.set_title('Multivariate Boxplot - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
ax.set_xticks([2, 6, 10])
ax.set_xticklabels(['A', 'B', 'C'])
# 添加图例
ax.legend([bp['boxes'][0] for bp in ax.get_children() if isinstance(bp, dict)],
variables, loc='upper right')
# 显示图形
plt.show()
Output:
这个例子展示了如何创建多变量箱线图。我们为每个变量绘制了三个组的箱线图,并使用不同的颜色来区分变量。
7. 水平箱线图
有时,水平方向的箱线图可能更适合某些数据的展示,特别是当组名较长时。
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = [np.random.normal(0, std, 100) for std in range(1, 6)]
labels = ['Group A with long name', 'Group B with longer name',
'Group C with even longer name', 'Group D', 'Group E']
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制水平箱线图
ax.boxplot(data, vert=False)
# 设置标题和标签
ax.set_title('Horizontal Boxplot - how2matplotlib.com')
ax.set_xlabel('Values')
ax.set_yticklabels(labels)
# 调整布局
plt.tight_layout()
# 显示图形
plt.show()
Output:
在这个例子中,我们使用 vert=False
参数来创建水平方向的箱线图。这种布局特别适合展示具有长标签的分组数据。
8. 添加统计信息
为了使箱线图更加信息丰富,我们可以在图表中添加一些统计信息,如均值或样本量。
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
np.random.seed(42)
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制箱线图
bp = ax.boxplot(data)
# 添加均值点
means = [np.mean(d) for d in data]
ax.scatter(range(1, len(data) + 1), means, color='red', marker='o', s=50, zorder=3)
# 添加样本量标签
for i, d in enumerate(data):
ax.text(i + 1, ax.get_ylim()[1], f'n={len(d)}',
horizontalalignment='center', verticalalignment='bottom')
# 设置标题和标签
ax.set_title('Boxplot with Statistics - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 添加图例
ax.legend([bp['medians'][0], bp['means'][0]], ['Median', 'Mean'], loc='lower right')
# 显示图形
plt.show()
在这个例子中,我们添加了红色的点来表示每组数据的均值,并在每个箱子上方标注了样本量。这些额外的信息可以帮助读者更好地理解数据的分布情况。
9. 处理异常值
箱线图的一个重要特性是它可以显示异常值。但有时,我们可能想要调整异常值的显示方式或完全隐藏它们。
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
np.random.seed(42)
data = [np.random.normal(0, 1, 100) for _ in range(3)]
# 添加一些异常值
data[0] = np.append(data[0], [5, 6, -5, -6])
data[1] = np.append(data[1], [7, 8, -7, -8])
data[2] = np.append(data[2], [9, 10, -9, -10])
# 创建图形和坐标轴
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
# 绘制默认箱线图
ax1.boxplot(data)
ax1.set_title('Default Boxplot\nhow2matplotlib.com')
# 绘制自定义异常值的箱线图
ax2.boxplot(data, flierprops=dict(marker='o', markerfacecolor='red', markersize=8,
linestyle='none'))
ax2.set_title('Customized Outliers\nhow2matplotlib.com')
# 设置标签
for ax in (ax1, ax2):
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 调整布局
plt.tight_layout()
# 显示图形
plt.show()
Output:
这个例子展示了如何自定义异常值的显示方式。在右侧的图表中,我们将异常值标记为红色的圆点,使其更加醒目。
10. 分组箱线图与其他图表的组合
有时,将箱线图与其他类型的图表结合使用可以提供更全面的数据视图。
10.1 箱线图与散点图的组合
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
np.random.seed(42)
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制箱线图
bp = ax.boxplot(data, positions=[1, 2, 3], widths=0.6)
# 添加散点图
for i, d in enumerate(data):
y = d
x = np.random.normal(i+1, 0.04, len(y))
ax.scatter(x, y, alpha=0.4)
# 设置标题和标签
ax.set_title('Boxplot with Scatter Plot - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
ax.set_xticks([1, 2, 3])
ax.set_xticklabels(['Group A', 'Group B', 'Group C'])
# 显示图形
plt.show()
Output:
这个例子展示了如何将箱线图与散点图结合,以同时显示数据的分布和个别数据点。
10.2 箱线图与小提琴图的组合
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
np.random.seed(42)
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制小提琴图
parts = ax.violinplot(data, positions=[1, 2, 3], showmeans=False, showmedians=False, showextrema=False)
for pc in parts['bodies']:
pc.set_facecolor('#D43F3A')
pc.set_edgecolor('black')
pc.set_alpha(0.7)
# 绘制箱线图
bp = ax.boxplot(data, positions=[1, 2, 3], widths=0.15, patch_artist=True)
for box in bp['boxes']:
box.set_facecolor('white')
box.set_edgecolor('black')
# 设置标题和标签
ax.set_title('Boxplot with Violin Plot - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
ax.set_xticks([1, 2, 3])
ax.set_xticklabels(['Group A', 'Group B', 'Group C'])
# 显示图形
plt.show()
Output:
这个例子展示了如何将箱线图与小提琴图结合。小提琴图可以更好地展示数据的分布密度,而箱线图则提供了关键的统计信息。
11. 动态更新的箱线图
在某些情况下,我们可能需要创建一个可以动态更新的箱线图,例如用于实时数据可视化。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
# 准备初始数据
data = [np.random.normal(0, 1, 100) for _ in range(3)]
# 创建图形和坐标轴
fig, ax = plt.subplots()
bp = ax.boxplot(data)
# 更新函数
def update(frame):
# 更新数据
for i in range(3):
data[i] = np.append(data[i][1:], np.random.normal(0, 1))
# 更新箱线图
for i, box in enumerate(bp['boxes']):
y = data[i]
box.set_ydata([np.percentile(y, 25), np.percentile(y, 25),
np.percentile(y, 75), np.percentile(y, 75)])
for i, median in enumerate(bp['medians']):
median.set_ydata([np.median(data[i]), np.median(data[i])])
for i, whisker in enumerate(bp['whiskers']):
if i % 2 == 0:
whisker.set_ydata([np.min(data[i//2]), np.percentile(data[i//2], 25)])
else:
whisker.set_ydata([np.percentile(data[i//2], 75), np.max(data[i//2])])
return bp
# 创建动画
ani = FuncAnimation(fig, update, frames=200, interval=50, blit=False)
# 设置标题和标签
ax.set_title('Dynamic Boxplot - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 显示动画
plt.show()
Output:
这个例子创建了一个动态更新的箱线图。每次更新时,我们移除每组数据的最旧的值,并添加一个新的随机值,然后重新计算并更新箱线图的各个组成部分。
12. 保存和导出箱线图
在创建完箱线图后,我们通常需要将其保存为图像文件或导出为其他格式。
import matplotlib.pyplot as plt
import numpy as np
# 准备数据
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制箱线图
ax.boxplot(data)
# 设置标题和标签
ax.set_title('Boxplot for Saving - how2matplotlib.com')
ax.set_xlabel('Groups')
ax.set_ylabel('Values')
# 保存为PNG文件
plt.savefig('boxplot_example.png', dpi=300, bbox_inches='tight')
# 保存为PDF文件
plt.savefig('boxplot_example.pdf', bbox_inches='tight')
# 保存为SVG文件
plt.savefig('boxplot_example.svg', bbox_inches='tight')
# 显示图形
plt.show()
Output:
这个例子展示了如何将箱线图保存为不同的文件格式。savefig()
函数允许我们指定文件名和格式。dpi
参数控制图像的分辨率,而 bbox_inches='tight'
确保图表的所有部分都被包含在保存的文件中。
总结
本文详细介绍了如何使用 Matplotlib 创建分组箱线图,涵盖了从基础概念到高级技巧的多个方面。我们学习了如何准备和格式化数据、自定义箱线图样式、处理分类数据和多变量数据、添加统计信息、处理异常值、结合其他图表类型,以及创建动态更新的箱线图。
通过掌握这些技巧,您可以创建出既信息丰富又视觉吸引的分组箱线图,有效地展示和比较不同组别的数据分布。记住,好的数据可视化不仅要准确传达信息,还要易于理解和解释。在实际应用中,根据您的具体需求和数据特征,灵活运用这些技巧来创建最适合您的箱线图。
最后,不要忘记探索 Matplotlib 的其他功能和图表类型,它们可能会为您的数据分析和可视化工作带来新的灵感和可能性。持续学习和实践将帮助您成为数据可视化的专家。