Python绘制小提琴图(附带实例)
小提琴图(Violin Plot)用于展示数值变量的分布情况。它结合了箱线图和核密度图的特点,可以同时显示数据的中位数、四分位数、离群值以及数据的密度分布。
小提琴图的主要组成部分包括:
小提琴图常用于比较不同组别之间数值变量的分布情况,可以帮助观察数据的集中趋势、离散程度以及异常值的存在情况。
【实例 1】通过绘制小提琴图展示不同车辆类型的高速公路里程分布情况。输入如下代码:

图 1 小提琴图1
【实例 2】使用 Seaborn 库绘制小提琴图,展示脑网络之间的相关性。输入如下代码:

图 2 小提琴图2
【实例 3】绘制小提琴图,其中一个采用默认样式,另一个采用自定义样式。输入如下代码:

图 3 小提琴图3
【实例 4】利用 Matplotlib 绘制不同样式的小提琴图。输入如下代码:

图 4 小提琴图4
【实例 5】利用 Proplot 库绘制箱线图与小提琴图。输入如下代码:
输出的结果如下图所示:

图5 小提琴图5
小提琴图的主要组成部分包括:
- 小提琴身体:由两个镜像的核密度估计曲线组成,展示了数据的密度分布情况。较宽的部分表示密度高,较窄的部分表示密度低;
- 白点/线条:表示数据的中位数和四分位数;
- 边缘:垂直的线条称为边缘,显示了数据的范围。离群值可以通过边缘以外的点来表示。
小提琴图常用于比较不同组别之间数值变量的分布情况,可以帮助观察数据的集中趋势、离散程度以及异常值的存在情况。
【实例 1】通过绘制小提琴图展示不同车辆类型的高速公路里程分布情况。输入如下代码:
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df = pd.read_csv("D:/DingJB/PyData/mpg_ggplot2.csv") # 导入数据 # 绘制小提琴图 plt.figure(figsize=(10, 6), dpi=80) sns.violinplot(x='class', y='hwy', data=df, scale='width', inner='quartile') plt.title('Highway Mileage by Vehicle Class', fontsize=16) # 图表修饰 plt.show()上述代码使用 Seaborn 的 violinplot() 函数绘制小提琴图,横轴表示车辆类型,纵轴表示高速公路里程。通过参数 scale='width' 设置小提琴图的宽度自适应箱线图的箱体宽度,inner='quartile' 设置小提琴内部显示四分位数。最后,设置图表的标题。输出的结果如下图所示。

图 1 小提琴图1
【实例 2】使用 Seaborn 库绘制小提琴图,展示脑网络之间的相关性。输入如下代码:
import seaborn as sns import matplotlib.pyplot as plt sns.set_theme(style="whitegrid") # 加载示例数据集,这是一个关于脑网络相关性的数据集 df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) # 提取特定子集的网络 used_networks = [1, 3, 4, 5, 6, 7, 8, 11, 12, 13, 16, 17] used_columns = (df.columns.get_level_values("network") .astype(int) .isin(used_networks)) df = df.loc[:, used_columns] # 计算相关矩阵并对网络进行平均 corr_df = df.corr().groupby(level="network").mean() corr_df.index = corr_df.index.astype(int) corr_df = corr_df.sort_index().T # 设置 Matplotlib 图形 f, ax = plt.subplots(figsize=(11, 6)) # 绘制小提琴图,带有比默认值更窄的带宽 sns.violinplot(data=corr_df, bw_adjust=.5, cut=1, linewidth=1, palette="Set3") # 完成图形 ax.set(ylim=(-.7, 1.05)) sns.despine(left=True, bottom=True) plt.show()上述代码从示例数据集中加载脑网络相关性数据后,选择特定的网络子集,并计算这些网络之间的相关性。最后绘制小提琴图来可视化这些相关性数据。输出的结果如下图所示。

图 2 小提琴图2
【实例 3】绘制小提琴图,其中一个采用默认样式,另一个采用自定义样式。输入如下代码:
import matplotlib.pyplot as plt import numpy as np # 定义函数:计算邻近值 def adjacent_values(vals, q1, q3): upper_adjacent_value = q3 + (q3 - q1) * 1.5 upper_adjacent_value = np.clip(upper_adjacent_value, q3, vals[-1]) lower_adjacent_value = q1 - (q3 - q1) * 1.5 lower_adjacent_value = np.clip(lower_adjacent_value, vals[0], q1) return lower_adjacent_value, upper_adjacent_value # 定义函数:设置坐标轴样式 def set_axis_style(ax, labels): ax.set_xticks(np.arange(1, len(labels) + 1), labels=labels) ax.set_xlim(0.25, len(labels) + 0.75) ax.set_xlabel('Sample name') # 创建测试数据 np.random.seed(19781101) # 固定随机数种子,以便结果可复现 data = [sorted(np.random.normal(0, std, 100)) for std in range(1, 5)] # 创建图形和子图 fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 4), sharey=True) # 绘制默认小提琴图 ax1.set_title('Default violin plot') ax1.set_ylabel('Observed values') ax1.violinplot(data) # 绘制自定义样式的小提琴图 ax2.set_title('Customized violin plot') parts = ax2.violinplot( data, showmeans=False, showmedians=False, showextrema=False, ) # 设置小提琴图的填充颜色和边缘颜色 for pc in parts['bodies']: pc.set_facecolor('#D43F3A') pc.set_edgecolor('black') pc.set_alpha(1) # 计算四分位数和范围 quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1) whiskers = np.array([ adjacent_values(sorted_array, q1, q3) for sorted_array, q1, q3 in zip(data, quartile1, quartile3) ]) inds = np.arange(1, len(medians) + 1) ax2.scatter(inds, medians, marker='o', color='white', s=30, zorder=3) ax2.vlines(inds, quartile1, quartile3, color='k', linestyle='--', lw=5) ax2.vlines(inds, whiskers[:, 0], whiskers[:, 1], color='k', linestyle='--', lw=1) # 设置坐标轴样式 labels = ['A', 'B', 'C', 'D'] for ax in [ax1, ax2]: set_axis_style(ax, labels) plt.subplots_adjust(bottom=0.15, wspace=0.05) plt.show()上述代码首先定义了两个函数,用于计算小提琴图的邻近值和设置坐标轴样式。然后生成了测试数据,创建了一个包含两个子图的图形,并分别绘制了默认样式和自定义样式的小提琴图。最后设置了水平间距和底部间距,并显示了图形。输出的结果如下图所示。

图 3 小提琴图3
【实例 4】利用 Matplotlib 绘制不同样式的小提琴图。输入如下代码:
import matplotlib.pyplot as plt import numpy as np np.random.seed(19781101) # 固定随机数种子,以便结果可复现 # 生成随机数据 fs = 10 # 字体大小 pos = [1, 2, 4, 5, 7, 8] # 每个小提琴图的位置 data = [np.random.normal(0, std, size=100) for std in pos] # 正态分布随机数据 # 创建图形和子图 fig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10, 6)) # 绘制自定义小提琴图 axs[0, 0].violinplot(data, pos, points=20, widths=0.3, showmeans=True, showextrema=True, showmedians=True) axs[0, 0].set_title('Custom violinplot 1', fontsize=fs) # 设置子图标题 axs[0, 1].violinplot(data, pos, points=40, widths=0.5, showmeans=True, showextrema=True, showmedians=True, bw_method='silverman') axs[0, 1].set_title('Custom violinplot 2', fontsize=fs) axs[0, 2].violinplot(data, pos, points=60, widths=0.7, showmeans=True, showextrema=True, showmedians=True, bw_method=0.5) axs[0, 2].set_title('Custom violinplot 3', fontsize=fs) axs[0, 3].violinplot(data, pos, points=60, widths=0.7, showmeans=True, showextrema=True, showmedians=True, bw_method=0.5, quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]]) axs[0, 3].set_title('Custom violinplot 4', fontsize=fs) axs[0, 4].violinplot(data[:-1], pos[:-1], points=60, widths=0.7, showmeans=True, showextrema=True, showmedians=True, quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5) axs[0, 4].set_title('Custom violinplot 5', fontsize=fs) axs[1, 0].violinplot(data, pos, points=80, vert=False, widths=0.7, showmeans=True, showextrema=True, showmedians=True) axs[1, 0].set_title('Custom violinplot 6', fontsize=fs) axs[1, 1].violinplot(data, pos, points=100, vert=False, widths=0.9, showmeans=True, showextrema=True, showmedians=True, bw_method='silverman') axs[1, 1].set_title('Custom violinplot 7', fontsize=fs) axs[1, 2].violinplot(data, pos, points=200, vert=False, widths=1.1, showmeans=True, showextrema=True, showmedians=True, bw_method=0.5) axs[1, 2].set_title('Custom violinplot 8', fontsize=fs) axs[1, 3].violinplot(data, pos, points=200, vert=False, widths=1.1, showmeans=True, showextrema=True, showmedians=True, quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]], bw_method=0.5) axs[1, 3].set_title('Custom violinplot 9', fontsize=fs) axs[1, 4].violinplot(data[:-1], pos[:-1], points=200, vert=False, widths=1.1, showmeans=True, showextrema=True, showmedians=True, quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5) axs[1, 4].set_title('Custom violinplot 10', fontsize=fs) # 隐藏每个子图的 Y 轴刻度标签 for ax in axs.flat: ax.set_yticklabels([]) fig.suptitle("Violin Plotting Examples") # 设置图形标题 fig.subplots_adjust(hspace=0.4) # 调整子图之间的垂直间距 plt.show()上述代码生成了 10 个自定义小提琴图的子图,并展示了不同参数设置下的效果。每个子图包含正态分布随机数据的小提琴图以及相应的设置,例如指定的点数、宽度、是否显示均值、极值和中位数等。输出的结果如下图所示:

图 4 小提琴图4
【实例 5】利用 Proplot 库绘制箱线图与小提琴图。输入如下代码:
import proplot as pplt import numpy as np import pandas as pd # 创建样本数据 N = 500 # 样本数量 state = np.random.RandomState(51423) # 创建随机状态实例,并指定随机数种子 # 生成服从正态分布的数据,每列的均值在逐渐增加,添加了一定程度的随机噪声 data1 = state.normal(size=(N, 5)) + 2 * (state.rand(N, 5) - 0.5) * np.arange(5) # 将数据转换为 DataFrame 格式,并指定列名 data1 = pd.DataFrame(data1, columns=pd.Index(list('abcde'), name='label')) # 生成随机数据,每行代表一个样本,列数为 7 data2 = state.rand(100, 7) data2 = pd.DataFrame(data2, columns=pd.Index(list('abcdefg'), name='label')) # 创建图形和子图,指定子图的排列方式 fig, axs = pplt.subplots((1, 1, 1), span=False) # 设置图形的格式 axs.format(abc='A.', titleloc='l', grid=False, suptitle='Boxes and violins demo') # 在第 1 个子图中绘制箱线图,指定显示均值和样本点的样式 ax = axs[0] obj1 = ax.box(data1, means=True, marker='x', meancolor='r', fillcolor='gray4') ax.format(title='Box plots') # 在第 2 个子图中绘制小提琴图,指定填充颜色,显示均值和样本点的样式 ax = axs[1] obj2 = ax.violin(data1, fillcolor='gray6', means=True, points=100) ax.format(title='Violin plots') # 在第 3 个子图中绘制箱线图,指定不同箱子的颜色 ax = axs[2] ax.boxh(data2, cycle='pastel2') ax.format(title='Multiple colors', ymargin=0.15)上述代码使用 ProPlot 库创建不同类型的箱线图和小提琴图,并对图形进行定制化处理:
- 首先,使用 ax.box() 函数在第一个子图中绘制了箱线图,并指定了显示均值和样本点的样式;
- 接着,在第二个子图中使用 ax.violin() 函数绘制了小提琴图,并指定了填充颜色、显示均值和样本点的样式;
- 最后,在第三个子图中使用 ax.boxh() 函数绘制了水平箱线图,并指定了不同箱子的颜色。
输出的结果如下图所示:

图5 小提琴图5