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

如何将seaborn kdeplot的高度归一化至1而非积分归一化?

问题

使用seaborn的「重叠密度(山脊图)」FacetGrid示例时,希望将kdeplot的高度归一化至1(峰值为1),而非默认的积分归一化(曲线下面积为1),但仅找到积分归一化的解决方案,如何实现?

原代码如下:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style="white", rc={"axes.facecolor": (0, 0, 0, 0)})

# Create the data
rs = np.random.RandomState(1979)
x = rs.randn(500)
g = np.tile(list("ABCDEFGHIJ"), 50)
df = pd.DataFrame(dict(x=x, g=g))
m = df.g.map(ord)
df["x"] += m

# Initialize the FacetGrid object
pal = sns.cubehelix_palette(10, rot=-.25, light=.7)
g = sns.FacetGrid(df, row="g", hue="g", aspect=15, height=.5, palette=pal)

# Draw the densities in a few steps
g.map(sns.kdeplot, "x",
      bw_adjust=.5, clip_on=False,
      fill=True, alpha=1, linewidth=1.5)
g.map(sns.kdeplot, "x", clip_on=False, color="w", lw=2, bw_adjust=.5)

# passing color=None to refline() uses the hue mapping
g.refline(y=0, linewidth=2, linestyle="-", color=None, clip_on=False)


# Define and use a simple function to label the plot in axes coordinates
def label(x, color, label):
    ax = plt.gca()
    ax.text(0, .2, label, fontweight="bold", color=color,
            ha="left", va="center", transform=ax.transAxes)


g.map(label, "x")

# Set the subplots to overlap
g.figure.subplots_adjust(hspace=-.25)

# Remove axes details that don't play well with overlap
g.set_titles("")
g.set(yticks=[], ylabel="")
g.despine(bottom=True, left=True)
解决方案

Seaborn的kdeplot没有直接设置峰值归一化的参数,需要自定义函数实现:

  • 使用scipy.stats.gaussian_kde计算每个分组的核密度估计
  • 将计算得到的密度值除以其最大值,使峰值归一化到1
  • 在FacetGrid中调用自定义函数替代原kdeplot

修改后的完整代码:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde

sns.set_theme(style="white", rc={"axes.facecolor": (0, 0, 0, 0)})

# Create the data
rs = np.random.RandomState(1979)
x = rs.randn(500)
g = np.tile(list("ABCDEFGHIJ"), 50)
df = pd.DataFrame(dict(x=x, g=g))
m = df.g.map(ord)
df["x"] += m

# Initialize the FacetGrid object
pal = sns.cubehelix_palette(10, rot=-.25, light=.7)
g = sns.FacetGrid(df, row="g", hue="g", aspect=15, height=.5, palette=pal)

# 自定义归一化到峰值为1的KDE绘图函数
def norm_kdeplot(x, color, label, bw_adjust=.5):
    ax = plt.gca()
    # 计算KDE
    kde = gaussian_kde(x, bw_method=bw_adjust)
    # 生成x轴采样点
    x_vals = np.linspace(x.min() - 1, x.max() + 1, 1000)
    # 计算密度值并归一化到峰值1
    y_vals = kde(x_vals)
    y_vals /= y_vals.max()
    # 绘制填充曲线
    ax.fill_between(x_vals, y_vals, color=color, alpha=1)
    # 绘制白色边框线
    ax.plot(x_vals, y_vals, color="w", lw=2)
    # 绘制黑色KDE线
    ax.plot(x_vals, y_vals, color=color, lw=1.5)

# 替换原kdeplot调用
g.map(norm_kdeplot, "x", bw_adjust=.5)

# passing color=None to refline() uses the hue mapping
g.refline(y=0, linewidth=2, linestyle="-", color=None, clip_on=False)

# Define and use a simple function to label the plot in axes coordinates
def label(x, color, label):
    ax = plt.gca()
    ax.text(0, .2, label, fontweight="bold", color=color,
            ha="left", va="center", transform=ax.transAxes)

g.map(label, "x")

# Set the subplots to overlap
g.figure.subplots_adjust(hspace=-.25)

# Remove axes details that don't play well with overlap
g.set_titles("")
g.set(yticks=[], ylabel="")
g.despine(bottom=True, left=True)

plt.show()
说明
  • 自定义函数norm_kdeplot中,通过y_vals /= y_vals.max()将密度曲线的峰值强制归一到1
  • 保留了原代码中填充、白色边框线、黑色曲线的绘制逻辑
  • bw_adjust参数和原代码保持一致,确保核密度的平滑度相同

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:05:29