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

如何让Seaborn的KDE曲线适配加权直方图数据?

问题:如何绘制加权直方图对应的加权KDE曲线?

我希望绘制两组数据的加权直方图及对应的加权Kernel Density Estimate(KDE)曲线:数据为DNA片段长度length(取值在(0, 1e8)区间的整数),按分类变量regions分为两组。目前使用seaborn的histplot时,设置weights='length'后直方图已变为加权形式,但内置的KDE曲线并未同步更新为加权版本。

附最小可复现代码:

import io
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

def plot_restriction_digest(df, out_file_base, weights):
     
    # Prevent the python icon from showing in the dock when the script is
    # running:
    matplotlib.use('Agg')
    
    sns.set_theme(style='ticks')
    f, ax = plt.subplots(figsize=(7, 5))
    sns.despine(f)

    hist = sns.histplot(data=df,
                        x='length',
                        hue='regions',
                        weights=weights,
                        stat='density',
                        common_norm=False,
                        bins=100,
                        log_scale=True,
                        kde=True,  # 此处KDE未应用权重
                        )
    sns.move_legend(hist, 'upper left')
    plt.savefig(f'{out_file_base}.pdf')
    return

testdata="""
1   all
1   all
2   all
2   all
2   all
3   all
4   captured
4   captured
5   captured
5   captured
5   captured
8   captured
"""

# 默认未加权图
df = pd.read_csv(io.StringIO(testdata), sep='\s+', header=None, names='length regions'.split())
plot_restriction_digest(df, 'test_tiny', None)

# 加权图(KDE未同步)
df = pd.read_csv(io.StringIO(testdata), sep='\s+', header=None, names='length regions'.split())
plot_restriction_digest(df, 'test_tiny_weighted', 'length')

print('Done.')

说明:

  • 两组数据对应“all”和“captured”两类基因组区域;
  • 真实数据含数千万行,需对数变换X轴区分分布;
  • 使用环境:Python 3.11.6,matplotlib-base 3.8.2,numpy 1.26.3,pandas 2.2.0,seaborn 0.13.1。

解决方案

seaborn内置的histplot(kde=True)不支持传递权重参数给KDE计算,因此需要手动计算每组的加权KDE并叠加到直方图上。具体步骤如下:

  1. 绘制加权直方图:去掉kde=True参数,先完成加权直方图的绘制;
  2. 计算加权KDE:使用scipy.stats.gaussian_kde(支持权重参数)对每个分组单独计算KDE;
  3. 叠加KDE曲线:在对数轴上生成合适的X值,计算对应的KDE密度,再绘制到直方图的轴上。

修改后的完整代码:

import io
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from scipy.stats import gaussian_kde  # 导入加权KDE计算工具

def plot_restriction_digest(df, out_file_base, weights):
     
    matplotlib.use('Agg')
    sns.set_theme(style='ticks')
    f, ax = plt.subplots(figsize=(7, 5))
    sns.despine(f)

    # 1. 绘制加权直方图,关闭内置KDE
    hist = sns.histplot(data=df,
                        x='length',
                        hue='regions',
                        weights=weights,
                        stat='density',
                        common_norm=False,
                        bins=100,
                        log_scale=True,
                        kde=False,  # 禁用内置KDE
                        )
    
    # 2. 为每个分组计算并绘制加权KDE
    # 获取分组和对应的颜色(保持与直方图一致)
    hue_groups = df['regions'].unique()
    palette = sns.color_palette()
    color_map = dict(zip(hue_groups, palette[:len(hue_groups)]))
    
    # 生成对数空间的X值,覆盖数据范围
    x_min = df['length'].min()
    x_max = df['length'].max()
    x_vals = np.logspace(np.log10(x_min), np.log10(x_max), 1000)
    
    for group in hue_groups:
        group_data = df[df['regions'] == group]
        lengths = group_data['length'].values
        # 获取权重,若无权重则设为1
        group_weights = group_data[weights].values if weights else np.ones(len(lengths))
        
        # 计算加权KDE
        kde = gaussian_kde(lengths, weights=group_weights)
        # 计算密度值,与histplot的stat='density'逻辑一致
        kde_vals = kde(x_vals)
        
        # 绘制KDE曲线,颜色与直方图对应
        ax.plot(x_vals, kde_vals, color=color_map[group], label=f'{group} KDE')
    
    # 调整图例
    sns.move_legend(hist, 'upper left')
    plt.savefig(f'{out_file_base}.pdf')
    return

testdata="""
1   all
1   all
2   all
2   all
2   all
3   all
4   captured
4   captured
5   captured
5   captured
5   captured
8   captured
"""

# 默认未加权图
df = pd.read_csv(io.StringIO(testdata), sep='\s+', header=None, names='length regions'.split())
plot_restriction_digest(df, 'test_tiny', None)

# 加权图(带加权KDE)
df = pd.read_csv(io.StringIO(testdata), sep='\s+', header=None, names='length regions'.split())
plot_restriction_digest(df, 'test_tiny_weighted', 'length')

print('Done.')

关键说明:

  • 使用scipy.stats.gaussian_kde直接处理原始数据和权重,确保KDE与加权直方图的统计逻辑一致;
  • 对数空间生成X值x_vals,适配log_scale=True的轴设置,保证曲线在对数轴上平滑显示;
  • 复用seaborn的调色板,让KDE曲线颜色与对应分组的直方图保持一致,提升可视化一致性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 09:07:36