You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在matplotlib中使小提琴图(violin plot)面积按样本计数缩放

解决方案

matplotlib 原生的 violinplot 接口没有直接提供按面积归一化的内置参数,你可以通过两种方式实现两个小提琴面积相等的需求:

方案1:手动调整violinplot返回的图形对象

plt.violinplot 会返回包含小提琴填充轮廓的对象,你可以计算两个轮廓的面积后,对水平方向的坐标做缩放调整,使二者面积一致:

import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
import numpy as np

year1 = [0.31686211, 0.39691523, 0.28396513, 0.35692174, 0.28013689, 0.40459902, 0.3020441, 0.34157149]
year2 = [0.18479367, 0.30268128, 0.25940495, 0.39964962, 0.32782555, 0.28997294, 0.31437325, 0.36121367]

# 绘制两个小提琴,放在同一个x坐标位置
vp1 = plt.violinplot(year1, positions=[1], showextrema=False)
vp2 = plt.violinplot(year2, positions=[1], showextrema=False)

# 计算多边形面积的工具函数(鞋带公式)
def calc_path_area(path):
    x, y = path.vertices[:, 0], path.vertices[:, 1]
    return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))

area1 = calc_path_area(vp1['bodies'][0].get_path())
area2 = calc_path_area(vp2['bodies'][0].get_path())

# 计算缩放系数,面积和水平缩放比例线性相关
if area1 > area2:
    scale = area2 / area1
    # 沿x轴缩放第一个小提琴,缩放原点为小提琴的中心x坐标1
    trans = transforms.Affine2D().scale(scale, 1, origin=(1, 0))
    vp1['bodies'][0].set_transform(plt.gca().transData + trans)
else:
    scale = area1 / area2
    trans = transforms.Affine2D().scale(scale, 1, origin=(1, 0))
    vp2['bodies'][0].set_transform(plt.gca().transData + trans)

# 调整样式区分两组
vp1['bodies'][0].set_facecolor('#1f77b4')
vp1['bodies'][0].set_alpha(0.6)
vp2['bodies'][0].set_facecolor('#ff7f0e')
vp2['bodies'][0].set_alpha(0.6)

plt.show()

方案2:自行计算KDE后绘制(更稳定可控)

你也可以手动计算两组数据的核密度估计,归一化到面积相等后直接用fill_betweenx绘制,不需要依赖violinplot的内置逻辑:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde

year1 = [0.31686211, 0.39691523, 0.28396513, 0.35692174, 0.28013689, 0.40459902, 0.3020441, 0.34157149]
year2 = [0.18479367, 0.30268128, 0.25940495, 0.39964962, 0.32782555, 0.28997294, 0.31437325, 0.36121367]

# 生成统一的y轴取值范围
y_range = np.linspace(min(*year1, *year2), max(*year1, *year2), 200)

# 计算两组的核密度
kde1 = gaussian_kde(year1)
kde2 = gaussian_kde(year2)
dens1 = kde1(y_range)
dens2 = kde2(y_range)

# 归一化密度,使两组积分(面积)相等
area1 = np.trapz(dens1, y_range)
area2 = np.trapz(dens2, y_range)
dens1 /= area1
dens2 /= area2

# 叠加绘制,左右分布避免完全重叠,也可调整为同一侧加透明度
plt.fill_betweenx(y_range, 1 - dens1, 1, color='#1f77b4', alpha=0.6, label='第一年')
plt.fill_betweenx(y_range, 1, 1 + dens2, color='#ff7f0e', alpha=0.6, label='第二年')

plt.xlim(0.5, 1.5)
plt.legend()
plt.show()

注:如果需要两个小提琴完全重叠在同一侧,只需要把两个fill_betweenx的x范围都设置为[1, 1+ dens]即可,调整透明度区分两组

内容的提问来源于stack exchange,提问作者louise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 02:48:03