首页 > 编程笔记 > Python笔记 阅读:2

Python绘制小提琴图(附带实例)

小提琴图(Violin Plot)用于展示数值变量的分布情况。它结合了箱线图和核密度图的特点,可以同时显示数据的中位数、四分位数、离群值以及数据的密度分布。

小提琴图的主要组成部分包括:
小提琴图常用于比较不同组别之间数值变量的分布情况,可以帮助观察数据的集中趋势、离散程度以及异常值的存在情况。

【实例 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 库创建不同类型的箱线图和小提琴图,并对图形进行定制化处理:
输出的结果如下图所示:


图5 小提琴图5

相关文章