You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何计算Seaborn distplot中多条KDE曲线各自的面积(common_norm=False场景)

如何计算Seaborn distplot中多条KDE曲线各自的面积(common_norm=False场景)

嗨,我来帮你把这个问题掰扯清楚!首先得纠正你一个可能的误解——你对common_norm=False的归一化逻辑搞反啦,这正是你疑惑的根源~

先搞懂common_norm的真实行为

你提到“设置common_norm=False时,所有曲线的面积之和应该为1”,这其实是**common_norm=True(默认值)**的规则!两个参数的真实区别是:

  • common_norm=True:所有KDE曲线一起归一化,总面积加起来是1,每条曲线的面积等于该组样本量占总样本量的比例(比如样本量300的组,面积就是300/总样本数)
  • common_norm=False:每条KDE曲线单独归一化,每条曲线自身的面积都是1,如果有3条曲线,总面积就是3,所以你积分出来每条面积一样是完全正确的!你的直觉可能混淆了“单独归一化”和“按样本量加权归一化”的逻辑~

正确计算每条KDE曲线面积的两种方法

方法1:从Seaborn绘图对象中提取曲线数据直接积分

当你用sns.distplot绘图后,可以直接从返回的Axes对象里提取每条KDE曲线的x、y数据,再用simps积分:

import seaborn as sns
import numpy as np
from scipy.integrate import simps
import matplotlib.pyplot as plt

np.random.seed(0)
# 生成模拟数据
low_peak_data = np.random.normal(loc=5, scale=0.5, size=100)
high_peak_data = np.random.normal(loc=7, scale=0.5, size=300)
# 补全双峰数据
bimodal_data = np.concatenate([np.random.normal(loc=3, scale=0.5, size=150), 
                               np.random.normal(loc=9, scale=0.5, size=150)])

# 绘制distplot,设置common_norm=False
sns.set_style("whitegrid")
fig, ax = plt.subplots()
sns.distplot(low_peak_data, kde=True, hist=False, label="Low Peak", ax=ax, common_norm=False)
sns.distplot(high_peak_data, kde=True, hist=False, label="High Peak", ax=ax, common_norm=False)
sns.distplot(bimodal_data, kde=True, hist=False, label="Bimodal", ax=ax, common_norm=False)
ax.legend()

# 提取每条KDE曲线的数据
kde_lines = [line for line in ax.get_lines() if line.get_label() != '_nolegend_']

# 计算每条曲线的面积
for line in kde_lines:
    x = line.get_xdata()
    y = line.get_ydata()
    area = simps(y, x)
    print(f"曲线「{line.get_label()}」的面积:{area:.4f}")

运行后你会发现每条曲线的面积都接近1,这完全符合common_norm=False的归一化规则。

方法2:手动计算KDE再积分

如果你不想依赖绘图对象,也可以用scipy.stats.gaussian_kde手动计算每组数据的KDE,再积分:

from scipy.stats import gaussian_kde

# 为每组数据创建KDE模型
low_kde = gaussian_kde(low_peak_data)
high_kde = gaussian_kde(high_peak_data)
bimodal_kde = gaussian_kde(bimodal_data)

# 生成覆盖所有数据的x轴范围,确保积分范围足够宽
x_min = min(low_peak_data.min(), high_peak_data.min(), bimodal_data.min())
x_max = max(low_peak_data.max(), high_peak_data.max(), bimodal_data.max())
x = np.linspace(x_min - 1, x_max + 1, 1000)

# 计算每个x对应的密度值
low_y = low_kde(x)
high_y = high_kde(x)
bimodal_y = bimodal_kde(x)

# 积分计算面积
low_area = simps(low_y, x)
high_area = simps(high_y, x)
bimodal_area = simps(bimodal_y, x)

print(f"Low Peak 面积:{low_area:.4f}")
print(f"High Peak 面积:{high_area:.4f}")
print(f"Bimodal 面积:{bimodal_area:.4f}")

这个方法的结果和方法1一致,因为gaussian_kde默认也是将单组数据的KDE归一化到面积1的。

如果你想要面积和样本量成正比

如果你的真实需求是让每条曲线的面积和样本量成正比(总面积为1),那只需要把common_norm设置为True(或者不设置,因为这是默认值),这时候积分出来的面积就是该组样本量占总样本量的比例,比如300样本的组面积就是300/(100+300+300)≈0.4286。

备注:内容来源于stack exchange,提问作者user13096842

火山引擎 最新活动