Matplotlib中violinplot()和boxplot()的区别及应用
参考:How is violinplot() Different from boxplot()
Matplotlib是Python中最常用的数据可视化库之一,它提供了多种图表类型来展示数据分布。其中,violinplot()和boxplot()都是用于展示数据分布的重要工具,但它们在展示方式和信息量上有着显著的区别。本文将详细介绍这两种图表的特点、区别以及各自的应用场景,并通过多个示例代码来展示它们的使用方法。
1. boxplot()简介
boxplot(),也称为箱线图,是一种用于显示一组数据分散情况的统计图,其显示的主要统计量包括最小值、下四分位数、中位数、上四分位数和最大值。
1.1 boxplot()的基本组成
一个标准的箱线图由以下几个部分组成:
- 箱体:表示数据的中间50%,即从下四分位数到上四分位数的范围。
- 中位线:在箱体内部的线,表示数据的中位数。
- 须线:从箱体延伸出去的线,通常延伸到最小值和最大值。
- 异常值:位于须线之外的数据点,通常用圆点表示。
1.2 boxplot()的基本使用
以下是一个使用boxplot()的简单示例:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data = np.random.normal(0, 1, 1000)
# 创建图形
plt.figure(figsize=(8, 6))
# 绘制箱线图
plt.boxplot(data)
# 设置标题和标签
plt.title('Boxplot Example - how2matplotlib.com')
plt.ylabel('Value')
# 显示图形
plt.show()
Output:
在这个示例中,我们生成了一组正态分布的随机数据,然后使用boxplot()函数绘制了箱线图。这个图表清晰地展示了数据的中位数、四分位数范围以及可能的异常值。
2. violinplot()简介
violinplot(),也称为小提琴图,是箱线图的一个变体,它不仅显示了数据的摘要统计信息,还展示了数据的概率密度。
2.1 violinplot()的基本组成
一个标准的小提琴图由以下几个部分组成:
- 密度轮廓:表示数据分布的概率密度,形状类似小提琴。
- 中位数:通常用一个点或线表示。
- 四分位数范围:通常在密度轮廓内部用线段表示。
- 最大值和最小值:通常用线段的端点表示。
2.2 violinplot()的基本使用
以下是一个使用violinplot()的简单示例:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data = np.random.normal(0, 1, 1000)
# 创建图形
plt.figure(figsize=(8, 6))
# 绘制小提琴图
plt.violinplot(data)
# 设置标题和标签
plt.title('Violinplot Example - how2matplotlib.com')
plt.ylabel('Value')
# 显示图形
plt.show()
Output:
在这个示例中,我们使用了与boxplot()示例相同的数据,但使用violinplot()函数绘制了小提琴图。这个图表不仅显示了数据的中位数和四分位数范围,还通过密度轮廓展示了数据的分布情况。
3. boxplot()和violinplot()的主要区别
虽然boxplot()和violinplot()都用于展示数据分布,但它们在信息展示和适用场景上有一些重要的区别:
3.1 信息量
- boxplot()主要展示数据的五个统计量(最小值、下四分位数、中位数、上四分位数、最大值),以及可能的异常值。
- violinplot()除了展示这些统计量外,还通过密度轮廓展示了数据的概率分布情况。
3.2 数据分布的可视化
- boxplot()对于展示数据的中心趋势和离散程度很有效,但无法显示数据的分布形状。
- violinplot()通过密度轮廓直观地展示了数据的分布形状,可以清楚地看出数据是否呈现正态分布、偏态分布等。
3.3 多组数据的比较
- boxplot()在比较多组数据时非常直观,特别是在比较中位数和四分位数范围时。
- violinplot()在比较多组数据的分布形状时更有优势,可以直观地看出不同组之间分布的差异。
3.4 适用的数据量
- boxplot()对数据量的要求相对较低,即使在数据量较小的情况下也能提供有用的信息。
- violinplot()通常需要较大的数据量才能准确地估计概率密度,在数据量较小时可能会产生误导。
4. boxplot()的进阶使用
4.1 绘制多组数据的箱线图
import matplotlib.pyplot as plt
import numpy as np
# 生成多组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(1, 1.5, 1000)
data3 = np.random.normal(-1, 0.5, 1000)
# 创建图形
plt.figure(figsize=(10, 6))
# 绘制多组箱线图
plt.boxplot([data1, data2, data3], labels=['Group 1', 'Group 2', 'Group 3'])
# 设置标题和标签
plt.title('Multiple Boxplots - how2matplotlib.com')
plt.ylabel('Value')
# 显示图形
plt.show()
Output:
这个示例展示了如何在一个图表中绘制多组数据的箱线图。通过这种方式,我们可以直观地比较不同组之间的数据分布情况。
4.2 自定义箱线图的样式
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data = np.random.normal(0, 1, 1000)
# 创建图形
plt.figure(figsize=(8, 6))
# 绘制自定义样式的箱线图
bp = plt.boxplot(data, patch_artist=True)
# 设置箱体颜色
for box in bp['boxes']:
box.set(facecolor='lightblue', edgecolor='navy')
# 设置中位线颜色
for median in bp['medians']:
median.set(color='red', linewidth=2)
# 设置须线颜色
for whisker in bp['whiskers']:
whisker.set(color='green', linewidth=1.5, linestyle='--')
# 设置标题和标签
plt.title('Customized Boxplot - how2matplotlib.com')
plt.ylabel('Value')
# 显示图形
plt.show()
Output:
这个示例展示了如何自定义箱线图的各个部分的样式,包括箱体颜色、中位线颜色和须线样式等。通过这种方式,我们可以创建更具视觉吸引力的箱线图。
5. violinplot()的进阶使用
5.1 绘制多组数据的小提琴图
import matplotlib.pyplot as plt
import numpy as np
# 生成多组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(1, 1.5, 1000)
data3 = np.random.normal(-1, 0.5, 1000)
# 创建图形
plt.figure(figsize=(10, 6))
# 绘制多组小提琴图
plt.violinplot([data1, data2, data3], showmeans=True, showmedians=True)
# 设置标题和标签
plt.title('Multiple Violinplots - how2matplotlib.com')
plt.ylabel('Value')
plt.xticks([1, 2, 3], ['Group 1', 'Group 2', 'Group 3'])
# 显示图形
plt.show()
Output:
这个示例展示了如何在一个图表中绘制多组数据的小提琴图。通过这种方式,我们可以直观地比较不同组之间的数据分布形状和密度。
5.2 自定义小提琴图的样式
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data = np.random.normal(0, 1, 1000)
# 创建图形
plt.figure(figsize=(8, 6))
# 绘制自定义样式的小提琴图
parts = plt.violinplot(data, showmeans=False, showmedians=False)
# 设置小提琴体的颜色和透明度
for pc in parts['bodies']:
pc.set_facecolor('lightblue')
pc.set_edgecolor('navy')
pc.set_alpha(0.7)
# 添加中位数和平均值
plt.scatter(1, np.median(data), marker='o', color='red', s=30, zorder=3)
plt.scatter(1, np.mean(data), marker='D', color='green', s=30, zorder=3)
# 设置标题和标签
plt.title('Customized Violinplot - how2matplotlib.com')
plt.ylabel('Value')
# 显示图形
plt.show()
Output:
这个示例展示了如何自定义小提琴图的样式,包括设置小提琴体的颜色和透明度,以及添加中位数和平均值的标记。通过这种方式,我们可以创建更具信息量和视觉吸引力的小提琴图。
6. boxplot()和violinplot()的组合使用
有时候,我们可能希望同时利用boxplot()和violinplot()的优点,这时可以考虑将两种图表结合使用。
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(1, 1.5, 1000)
data3 = np.random.normal(-1, 0.5, 1000)
# 创建图形
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(12, 6))
# 绘制箱线图
bp = ax1.boxplot([data1, data2, data3], patch_artist=True)
for box in bp['boxes']:
box.set(facecolor='lightblue', edgecolor='navy')
# 绘制小提琴图
vp = ax2.violinplot([data1, data2, data3], showmeans=True, showmedians=True)
for body in vp['bodies']:
body.set_facecolor('lightgreen')
body.set_edgecolor('darkgreen')
body.set_alpha(0.7)
# 设置标题和标签
ax1.set_title('Boxplot - how2matplotlib.com')
ax2.set_title('Violinplot - how2matplotlib.com')
ax1.set_ylabel('Value')
ax2.set_ylabel('Value')
ax1.set_xticklabels(['Group 1', 'Group 2', 'Group 3'])
ax2.set_xticklabels(['Group 1', 'Group 2', 'Group 3'])
# 调整子图间距
plt.tight_layout()
# 显示图形
plt.show()
Output:
这个示例展示了如何在同一个图形中并排绘制箱线图和小提琴图。通过这种方式,我们可以同时利用两种图表的优点,既能看到数据的关键统计量,又能直观地了解数据的分布形状。
7. 选择使用boxplot()还是violinplot()的考虑因素
在选择使用boxplot()还是violinplot()时,需要考虑以下几个因素:
- 数据量:如果数据量较小,boxplot()可能更合适,因为violinplot()在数据量小时可能无法准确估计概率密度。
-
分布形状:如果你关心数据的具体分布形状(如是否存在多峰、偏态等),violinplot()会更有帮助。
-
异常值:如果你主要关注异常值的检测,boxplot()可能更直观。
-
多组比较:对于多组数据的比较,两种图表都有各自的优势。boxplot()在比较中位数和四分位数范围时更直观,而violinplot()在比较整体分布形状时更有优势。
-
受众:考虑你的目标受众对这两种图表的熟悉程度。boxplot()通常更为常见,可能更容易被广泛理解。
-
可视化目的:如果你的主要目的是展示数据的集中趋势和离散程度,boxplot()可能就足够了。如果你想展示更多的分布细节,violinplot()会更合适。
8. boxplot()和violinplot()的高级应用
8.1 结合散点图的箱线图
有时,我们可能希望在箱线图上直接展示原始数据点,这可以通过结合散点图来实现:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
np.random.seed(42)
data1 = np.random.normal(0, 1, 100)
data2 = np.random.normal(1, 1.5, 100)
data3 = np.random.normal(-1, 0.5, 100)
# 创建图形
plt.figure(figsize=(10, 6))
# 绘制箱线图
bp = plt.boxplot([data1, data2, data3], patch_artist=True)
# 自定义箱线图样式
colors = ['lightblue', 'lightgreen', 'lightpink']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)
# 添加散点
for i, data in enumerate([data1, data2, data3], 1):
x = np.random.normal(i, 0.04, len(data))
plt.scatter(x, data, alpha=0.3)
# 设置标题和标签
plt.title('Boxplot with Scatter - how2matplotlib.com')
plt.ylabel('Value')
plt.xticks([1, 2, 3], ['Group 1', 'Group 2', 'Group 3'])
# 显示图形
plt.show()
Output:
这个示例展示了如何在箱线图上添加散点图,以同时显示原始数据点和统计摘要。这种方法特别适用于数据量较小的情况,可以让读者更全面地了解数据分布。
8.2 分组小提琴图
当我们需要比较多个分类变量下的数值分布时,可以使用分组小提琴图:
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'], 300),
'subgroup': np.tile(np.repeat(['X', 'Y'], 150), 3),
'value': np.concatenate([
np.random.normal(0, 1, 300),
np.random.normal(1, 1.5, 300),
np.random.normal(-1, 0.5, 300)
])
})
# 创建图形
plt.figure(figsize=(12, 6))
# 绘制分组小提琴图
vp = plt.violinplot([df[(df['group'] == g) & (df['subgroup'] == s)]['value']
for g in ['A', 'B', 'C'] for s in ['X', 'Y']])
# 自定义小提琴图样式
colors = ['lightblue', 'lightgreen']
for i, pc in enumerate(vp['bodies']):
pc.set_facecolor(colors[i % 2])
pc.set_edgecolor('black')
pc.set_alpha(0.7)
# 设置标题和标签
plt.title('Grouped Violinplot - how2matplotlib.com')
plt.ylabel('Value')
plt.xticks(range(1, 7), ['A-X', 'A-Y', 'B-X', 'B-Y', 'C-X', 'C-Y'])
# 显示图形
plt.show()
Output:
这个示例展示了如何创建分组小提琴图,用于比较多个分类变量(在这个例子中是 ‘group’ 和 ‘subgroup’)下的数值分布。这种图表可以有效地展示复杂的分组数据结构。
9. boxplot()和violinplot()在不同数据分布下的表现
为了更好地理解boxplot()和violinplot()在不同数据分布下的表现差异,我们可以创建一个包含多种分布的对比图:
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
# 创建不同的数据分布
np.random.seed(42)
normal = np.random.normal(0, 1, 1000)
skewed = np.random.exponential(1, 1000)
bimodal = np.concatenate([np.random.normal(-1, 0.5, 500), np.random.normal(1, 0.5, 500)])
uniform = np.random.uniform(-2, 2, 1000)
# 创建图形
fig, axes = plt.subplots(2, 4, figsize=(16, 8))
# 绘制不同分布的箱线图和小提琴图
distributions = [normal, skewed, bimodal, uniform]
titles = ['Normal', 'Skewed', 'Bimodal', 'Uniform']
for i, (data, title) in enumerate(zip(distributions, titles)):
# 箱线图
axes[0, i].boxplot(data)
axes[0, i].set_title(f'{title} - Boxplot')
# 小提琴图
axes[1, i].violinplot(data)
axes[1, i].set_title(f'{title} - Violinplot')
# 调整子图间距
plt.tight_layout()
# 添加总标题
fig.suptitle('Comparison of Boxplots and Violinplots - how2matplotlib.com', fontsize=16)
plt.subplots_adjust(top=0.9)
# 显示图形
plt.show()
Output:
这个示例创建了一个包含四种不同数据分布(正态分布、偏态分布、双峰分布和均匀分布)的对比图,每种分布都用箱线图和小提琴图表示。通过这个对比,我们可以清楚地看到:
- 对于正态分布,boxplot和violinplot都能很好地表示数据的中心趋势和离散程度,但violinplot更能展示出数据的对称性。
-
对于偏态分布,violinplot能更直观地展示数据的不对称性,而boxplot可能会掩盖这一特征。
-
对于双峰分布,violinplot能清晰地显示出两个峰值,而boxplot则无法反映这一特征。
-
对于均匀分布,violinplot能展示出数据在整个范围内的均匀分布特性,而boxplot只能显示出中位数和四分位数范围。
10. boxplot()和violinplot()的性能考虑
在处理大量数据或需要实时更新图表时,性能也是一个需要考虑的因素。一般来说,boxplot()的计算复杂度较低,因为它只需要计算几个简单的统计量。相比之下,violinplot()需要估计概率密度函数,计算复杂度较高。
以下是一个简单的性能对比示例:
import matplotlib.pyplot as plt
import numpy as np
import time
# 创建不同大小的数据集
sizes = [1000, 10000, 100000, 1000000]
boxplot_times = []
violinplot_times = []
for size in sizes:
data = np.random.normal(0, 1, size)
# 测量boxplot的时间
start = time.time()
plt.boxplot(data)
boxplot_times.append(time.time() - start)
plt.close()
# 测量violinplot的时间
start = time.time()
plt.violinplot(data)
violinplot_times.append(time.time() - start)
plt.close()
# 绘制性能对比图
plt.figure(figsize=(10, 6))
plt.plot(sizes, boxplot_times, label='Boxplot', marker='o')
plt.plot(sizes, violinplot_times, label='Violinplot', marker='s')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Data Size')
plt.ylabel('Execution Time (seconds)')
plt.title('Performance Comparison: Boxplot vs Violinplot - how2matplotlib.com')
plt.legend()
plt.grid(True)
plt.show()
Output:
这个示例比较了boxplot()和violinplot()在不同数据大小下的执行时间。通常,你会发现violinplot()的执行时间随数据量增加而增加得更快。
结论
boxplot()和violinplot()都是强大的数据可视化工具,各有其优势和适用场景。boxplot()简洁明了,适合快速了解数据的中心趋势和离散程度,特别适合多组数据的比较。violinplot()则能提供更丰富的分布信息,适合深入分析数据的分布特征。
在实际应用中,选择使用哪种图表取决于多个因素,包括数据特征、分析目的、目标受众等。有时候,同时使用两种图表或将它们结合使用可能是最佳选择,以便全面地展示数据特征。
无论选择哪种图表,重要的是要确保图表清晰、易懂,并能有效地传达数据中的关键信息。通过合理使用这些可视化工具,我们可以更好地理解和解释复杂的数据集,从而做出更明智的决策。