Matplotlib中如何在同一坐标轴上绘制两个箱线图
参考:Combining Two Boxplots With the Same Axes
箱线图是一种用于展示数据分布情况的统计图表,它可以直观地显示出一组数据的中位数、四分位数范围、异常值等重要统计信息。在数据分析和可视化中,我们经常需要比较不同组或条件下的数据分布。Matplotlib作为Python中最流行的绘图库之一,提供了强大的功能来创建和自定义箱线图。本文将详细介绍如何在Matplotlib中在同一坐标轴上绘制两个箱线图,以便进行直观的数据比较和分析。
1. 箱线图的基本概念
在深入探讨如何在同一坐标轴上绘制两个箱线图之前,我们先来回顾一下箱线图的基本概念。
箱线图,也称为盒须图,是一种用作显示一组数据分散情况资料的统计图。它能显示出一组数据的最大值、最小值、中位数、下四分位数及上四分位数。
箱线图的主要组成部分包括:
– 箱子:代表数据的四分位范围,即从第25百分位数到第75百分位数。
– 中位线:表示数据的中位数。
– 须:延伸到箱子外的线,通常延伸到最小和最大值,但不包括异常值。
– 异常值:通常以单独的点表示,位于须之外。
让我们通过一个简单的例子来创建一个基本的箱线图:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
np.random.seed(42)
data = np.random.normal(100, 20, 200)
# 创建箱线图
plt.figure(figsize=(8, 6))
plt.boxplot(data)
plt.title('Basic Boxplot - how2matplotlib.com')
plt.ylabel('Values')
plt.show()
Output:
在这个例子中,我们首先导入了必要的库:Matplotlib的pyplot模块和NumPy。然后,我们生成了一组随机数据,并使用plt.boxplot()
函数创建了一个简单的箱线图。figsize
参数用于设置图形的大小,title
和ylabel
分别用于设置图表标题和y轴标签。
2. 在同一坐标轴上绘制两个箱线图的方法
现在,让我们探讨如何在同一坐标轴上绘制两个箱线图。这种方法通常用于比较两组数据的分布情况。
2.1 使用plt.boxplot()函数
最直接的方法是使用plt.boxplot()
函数,并将两组数据作为列表传递给它。这里有一个例子:
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
# 创建箱线图
plt.figure(figsize=(10, 6))
plt.boxplot([data1, data2], labels=['Group 1', 'Group 2'])
plt.title('Two Boxplots on Same Axes - how2matplotlib.com')
plt.ylabel('Values')
plt.show()
Output:
在这个例子中,我们生成了两组正态分布的随机数据。然后,我们将这两组数据作为一个列表传递给plt.boxplot()
函数。labels
参数用于为每个箱线图添加标签。
2.2 使用面向对象的方法
虽然使用pyplot接口很方便,但面向对象的方法提供了更多的灵活性和控制。以下是使用面向对象方法创建两个箱线图的例子:
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
# 创建图形和坐标轴对象
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制箱线图
ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'])
ax.set_title('Two Boxplots Using OOP - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们使用plt.subplots()
创建了一个图形对象和一个坐标轴对象。然后,我们使用坐标轴对象的boxplot()
方法来绘制箱线图。这种方法的优势在于它允许我们更精细地控制图形的各个方面。
3. 自定义箱线图的外观
现在我们已经知道如何在同一坐标轴上绘制两个箱线图,让我们来探讨一些自定义箱线图外观的方法。
3.1 改变箱线图的颜色
我们可以通过设置boxplot()
函数的参数来改变箱线图的颜色。以下是一个例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
boxprops = dict(facecolor='lightblue', edgecolor='navy')
whiskerprops = dict(color='navy')
capprops = dict(color='navy')
medianprops = dict(color='red')
ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'],
boxprops=boxprops, whiskerprops=whiskerprops,
capprops=capprops, medianprops=medianprops)
ax.set_title('Customized Boxplots - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
在这个例子中,我们定义了几个字典来设置箱子、须、帽和中位数线的颜色和样式。然后,我们将这些字典作为参数传递给boxplot()
函数。
3.2 添加抖动点
有时,我们可能想在箱线图上显示原始数据点。我们可以使用bplot
对象的scatter()
方法来实现这一点:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
bplot = ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'], patch_artist=True)
colors = ['lightblue', 'lightgreen']
for patch, color in zip(bplot['boxes'], colors):
patch.set_facecolor(color)
for i, d in enumerate([data1, data2]):
y = d
x = np.random.normal(i+1, 0.04, len(y))
ax.scatter(x, y, alpha=0.4)
ax.set_title('Boxplots with Jittered Points - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们首先创建了常规的箱线图。然后,我们使用一个循环来为每组数据添加抖动点。np.random.normal()
函数用于在x轴方向上添加一些随机抖动,使点不会完全重叠。
3.3 添加均值点
除了中位数,我们可能还想在箱线图上显示每组数据的均值。这可以通过在箱线图上叠加散点图来实现:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
bplot = ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'])
means = [np.mean(data1), np.mean(data2)]
ax.scatter([1, 2], means, color='red', marker='D', s=80, label='Mean')
ax.set_title('Boxplots with Mean Points - how2matplotlib.com')
ax.set_ylabel('Values')
ax.legend()
plt.show()
Output:
在这个例子中,我们首先计算了每组数据的均值,然后使用scatter()
方法在相应的位置添加了菱形标记。我们还添加了一个图例来说明这些点代表均值。
4. 处理多组数据
虽然我们主要讨论的是两个箱线图的情况,但Matplotlib也可以轻松处理更多组的数据。以下是一个包含四组数据的例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
data3 = np.random.normal(110, 15, 200)
data4 = np.random.normal(95, 30, 200)
fig, ax = plt.subplots(figsize=(12, 6))
bplot = ax.boxplot([data1, data2, data3, data4],
labels=['Group 1', 'Group 2', 'Group 3', 'Group 4'],
patch_artist=True)
colors = ['lightblue', 'lightgreen', 'pink', 'lightyellow']
for patch, color in zip(bplot['boxes'], colors):
patch.set_facecolor(color)
ax.set_title('Multiple Boxplots - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们创建了四组数据,并将它们全部传递给boxplot()
函数。我们还为每个箱子设置了不同的颜色,以便更容易区分不同的组。
5. 添加统计信息
在比较不同组的数据时,我们可能想要在图上显示一些统计信息,比如p值。以下是一个如何在箱线图上添加统计信息的例子:
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
bplot = ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'])
# 执行t检验
t_stat, p_value = stats.ttest_ind(data1, data2)
# 添加p值信息
ax.text(0.5, 1.05, f'p-value: {p_value:.4f}', transform=ax.transAxes, ha='center')
ax.set_title('Boxplots with Statistical Information - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们使用SciPy的stats.ttest_ind()
函数执行了一个独立样本t检验,然后使用ax.text()
方法将p值添加到图表上方。
6. 创建水平箱线图
到目前为止,我们创建的所有箱线图都是垂直的。但是,在某些情况下,水平箱线图可能更适合,特别是当你有很长的标签时。以下是如何创建水平箱线图的例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
bplot = ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'], vert=False)
ax.set_title('Horizontal Boxplots - how2matplotlib.com')
ax.set_xlabel('Values')
plt.show()
Output:
在这个例子中,我们只需要在boxplot()
函数中设置vert=False
参数就可以创建水平箱线图。注意,我们现在使用set_xlabel()
而不是set_ylabel()
来设置值轴的标签。
7. 处理异常值
箱线图的一个重要特性是它可以显示异常值。默认情况下,Matplotlib将任何超出1.5倍四分位范围的数据点视为异常值。但是,我们可以自定义这个行为。以下是一个例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
# 添加一些极端值
data1 = np.append(data1, [200, 210])
data2 = np.append(data2, [0, 10])
fig, ax = plt.subplots(figsize=(10, 6))
bplot = ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'],
whis=1.5, showfliers=True, flierprops=dict(marker='o', markersize=10))
ax.set_title('Boxplots with Custom Outlier Display - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们首先添加了一些极端值到我们的数据中。然后,我们使用whis
参数来设置异常值的阈值(这里设置为1.5,这实际上是默认值)。showfliers=True
参数确保异常值被显示,而flierprops
参数允许我们自定义异常值点的外观。
8. 添加注释和标记
有时,我们可能想要在箱线图上添加一些额外的注释或标记,以突出显示某些特定的数据点或范围。以下是一个如何在箱线图上添加注释和标记的例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
bplot = ax.boxplot([data1, data2], labels=['Group 1', 'Group 2'])
# 添加注释
ax.annotate('Interesting point', xy=(1, np.max(data1)), xytext=(1.2, np.max(data1)+20),
arrowprops=dict(facecolor='black', shrink=0.05))
# 添加水平线
ax.axhline(y=np.median([np.median(data1), np.median(data2)]), color='r', linestyle='--')
ax.set_title('Boxplots with Annotations and Markers - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们使用ax.annotate()
方法添加了一个带箭头的注释,指向第一组数据的最大值。我们还使用ax.axhline()
方法添加了一条水平虚线,表示两组数据中位数的平均值。
9. 创建分组箱线图
当我们有多个因素需要比较时,分组箱线图可能会很有用。以下是一个创建分组箱线图的例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = [np.random.normal(100, 20, 200), np.random.normal(90, 25, 200)]
data2 = [np.random.normal(110, 15, 200), np.random.normal(95, 30, 200)]
fig, ax = plt.subplots(figsize=(12, 6))
positions = [1, 2, 4, 5]
bplot1 = ax.boxplot(data1, positions=[1, 2], widths=0.6, patch_artist=True)
bplot2 = ax.boxplot(data2, positions=[4, 5], widths=0.6, patch_artist=True)
# 自定义颜色
colors = ['lightblue', 'lightgreen']
for bplot in (bplot1, bplot2):
for patch, color in zip(bplot['boxes'], colors):
patch.set_facecolor(color)
ax.set_xticks([1.5, 4.5])
ax.set_xticklabels(['Group A', 'Group B'])
ax.set_title('Grouped Boxplots - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们创建了两组箱线图,每组包含两个子组。我们使用positions
参数来控制每个箱线图的位置,并使用widths
参数来调整箱子的宽度。我们还自定义了x轴的刻度和标签,以更好地表示分组。
10. 结合小提琴图和箱线图
小提琴图是箱线图的一个有趣的变体,它可以显示数据的概率密度。我们可以将小提琴图和箱线图结合起来,以提供更丰富的数据可视化。以下是一个例子:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig, ax = plt.subplots(figsize=(10, 6))
parts = ax.violinplot([data1, data2], showmeans=False, showmedians=False, showextrema=False)
for pc in parts['bodies']:
pc.set_facecolor('lightblue')
pc.set_edgecolor('black')
pc.set_alpha(0.7)
ax.boxplot([data1, data2], positions=[1, 2], widths=0.2, showfliers=False)
ax.set_xticks([1, 2])
ax.set_xticklabels(['Group 1', 'Group 2'])
ax.set_title('Combined Violin and Box Plots - how2matplotlib.com')
ax.set_ylabel('Values')
plt.show()
Output:
在这个例子中,我们首先使用violinplot()
函数创建小提琴图,然后在其上叠加了箱线图。小提琴图显示了数据的分布形状,而箱线图则提供了关键的统计信息。
11. 使用Seaborn创建箱线图
虽然Matplotlib提供了强大的绘图功能,但有时使用更高级的库如Seaborn可以让我们更容易地创建复杂的统计图表。Seaborn是基于Matplotlib的,但提供了更简洁的API和更美观的默认样式。以下是使用Seaborn创建箱线图的例子:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
df = pd.DataFrame({
'Group': ['Group 1']*200 + ['Group 2']*200,
'Value': np.concatenate([data1, data2])
})
plt.figure(figsize=(10, 6))
sns.boxplot(x='Group', y='Value', data=df)
plt.title('Seaborn Boxplot - how2matplotlib.com')
plt.show()
Output:
在这个例子中,我们首先创建了一个pandas DataFrame来组织我们的数据。然后,我们使用Seaborn的boxplot()
函数来创建箱线图。Seaborn自动处理了数据的分组,并提供了美观的默认样式。
12. 创建交互式箱线图
对于一些应用场景,交互式图表可能更有用。我们可以使用Plotly库来创建交互式的箱线图。以下是一个例子:
import plotly.graph_objects as go
import numpy as np
np.random.seed(42)
data1 = np.random.normal(100, 20, 200)
data2 = np.random.normal(90, 25, 200)
fig = go.Figure()
fig.add_trace(go.Box(y=data1, name='Group 1'))
fig.add_trace(go.Box(y=data2, name='Group 2'))
fig.update_layout(title='Interactive Boxplot - how2matplotlib.com',
yaxis_title='Values')
fig.show()
这个例子使用Plotly创建了一个交互式的箱线图。用户可以悬停在图表上查看详细信息,放大缩小,甚至保存图表为图片。
总结
在本文中,我们深入探讨了如何在Matplotlib中在同一坐标轴上绘制两个箱线图,并介绍了多种自定义和增强箱线图的方法。我们学习了如何改变箱线图的颜色、添加数据点、显示均值、处理异常值、添加注释和标记等。我们还探讨了如何创建分组箱线图、结合小提琴图和箱线图,以及如何使用其他库如Seaborn和Plotly来创建箱线图。
箱线图是一种强大的数据可视化工具,可以帮助我们快速理解和比较不同组的数据分布。通过本文介绍的技术,你应该能够创建出既信息丰富又视觉吸引的箱线图,以更好地展示和分析你的数据。
记住,数据可视化是一门艺术,也是一门科学。虽然我们提供了许多技术和方法,但最终如何应用这些技术来最好地展示你的数据,还需要根据具体的数据和目标来决定。不断实践和尝试不同的方法,你会发现最适合你的数据和受众的可视化方式。