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

如何用Seaborn基于密度函数复刻KDE图样式(含曲线下填充)

问题描述

我非常喜欢Seaborn的KDE图样式,现在我有一组由密度函数生成的数据集:x是样本的x值,y是对应的密度函数值,μσ是用于分组的分类变量(用来区分不同的密度分布,示例里是正态分布的均值和标准差)。我能用Seaborn的lineplot绘制曲线,但没法直接实现KDE图那样的曲线下填充效果。目前我是用lineplot加手动填充的方式实现了类似效果,但这只是临时方案,想知道怎么用Seaborn原生的方法更自然地复刻这种KDE图样式(包含曲线下区域填充)。

现有实现代码:

import numpy as np
import scipy as sp
import pandas as pd

from scipy.stats import norm

import matplotlib.pyplot as plt
import seaborn as sns


num_grid_pts  = 1000
val_μ         = [0, -1, 1, 0]
val_σ         = [1, 2, 3, 4]

num_var = len(val_μ) # variations

x = np.linspace(-10, 10, num_grid_pts)
P = np.zeros((num_grid_pts, num_var)) # PDF

μσ = [f'μ = {μ}, σ = {σ}' for μ, σ in zip(val_μ, val_σ)]

for ii, (μ, σ) in enumerate(zip(val_μ, val_σ)):
  randVar  = norm(μ, σ)
  P[:, ii] = randVar.pdf(x)

df_P = pd.DataFrame(data = {'x': np.tile(x, num_var), 'PDF': P.flatten('F'), 'μσ': np.repeat(μσ, len(x))})

f, ax = plt.subplots(figsize=(15, 10))
sns.lineplot(data=df_P, x='x', y='PDF', hue='μσ', ax=ax)
plot_lines = ax.get_lines()
for ii in range(num_var):
  ax.fill_between(x=plot_lines[ii].get_xdata(), y1=plot_lines[ii].get_ydata(), alpha=0.25, color=plot_lines[ii].get_color())
ax.set_title(f'Normal Distribution')
ax.set_xlabel(f'Value')
ax.set_ylabel(f'Probability')

plt.show()
解决方案

Seaborn本身没有原生的带填充分组线图API,但可以通过两种更贴合Seaborn设计逻辑的方式替代手动遍历线条的临时方案:

方法1:利用FacetGrid批量绘制填充曲线

借助relplot创建FacetGrid,通过分组映射直接关联颜色,避免手动提取线条:

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

num_grid_pts = 1000
val_μ = [0, -1, 1, 0]
val_σ = [1, 2, 3, 4]
μσ = [f'μ = {μ}, σ = {σ}' for μ, σ in zip(val_μ, val_σ)]

x = np.linspace(-10, 10, num_grid_pts)
P = np.zeros((num_grid_pts, len(val_μ)))
for ii, (μ, σ) in enumerate(zip(val_μ, val_σ)):
    P[:, ii] = norm(μ, σ).pdf(x)

df_P = pd.DataFrame({
    'x': np.tile(x, len(val_μ)),
    'PDF': P.flatten('F'),
    'μσ': np.repeat(μσ, len(x))
})

# 创建带hue分组的线图FacetGrid
g = sns.relplot(data=df_P, x='x', y='PDF', hue='μσ', kind='line', 
                figsize=(15,10), legend='full')

# 遍历分组,用Seaborn自动分配的颜色填充
for name, group in df_P.groupby('μσ'):
    color = g._hue_map[name]
    g.ax.fill_between(group['x'], group['PDF'], alpha=0.25, color=color)

g.ax.set_title('Normal Distribution')
g.ax.set_xlabel('Value')
g.ax.set_ylabel('Probability')
plt.show()

方法2:直接使用Seaborn调色板映射颜色

通过color_palette获取Seaborn的分组配色,直接按分组填充,逻辑更直观:

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

num_grid_pts = 1000
val_μ = [0, -1, 1, 0]
val_σ = [1, 2, 3, 4]
μσ = [f'μ = {μ}, σ = {σ}' for μ, σ in zip(val_μ, val_σ)]

x = np.linspace(-10, 10, num_grid_pts)
P = np.zeros((num_grid_pts, len(val_μ)))
for ii, (μ, σ) in enumerate(zip(val_μ, val_σ)):
    P[:, ii] = norm(μ, σ).pdf(x)

df_P = pd.DataFrame({
    'x': np.tile(x, len(val_μ)),
    'PDF': P.flatten('F'),
    'μσ': np.repeat(μσ, len(x))
})

f, ax = plt.subplots(figsize=(15,10))
# 绘制线图,获取对应分组数的调色板
sns.lineplot(data=df_P, x='x', y='PDF', hue='μσ', ax=ax)
palette = sns.color_palette(n_colors=len(μσ))

# 按分组填充颜色
for idx, (name, group) in enumerate(df_P.groupby('μσ')):
    ax.fill_between(group['x'], group['PDF'], alpha=0.25, color=palette[idx])

ax.set_title('Normal Distribution')
ax.set_xlabel('Value')
ax.set_ylabel('Probability')
plt.show()

关键说明

  • 两种方法都避免了直接操作Axes对象的线条,完全利用Seaborn的分组和配色系统,保持图例与填充颜色的一致性
  • 方法1的_hue_map是FacetGrid内部维护的分组-颜色映射,能完美匹配Seaborn的图例配色
  • 方法2直接调用Seaborn的调色板生成函数,逻辑更清晰,适合快速实现

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 05:30:43