如何让手动实现的KDE与sns.kdeplot输出的可视化结果一致?
两种KDE绘图结果差异的核心原因
- 等高线层级计算逻辑完全不同:这是最主要的差异来源。
sns.kdeplot默认的层级是基于密度的分位数设置,每个层级对应可覆盖指定比例样本的密度阈值,默认10个层级对应0~0.9的分位数区间,等高线是按数据分布的覆盖比例划分的。而你调用plt.contourf(levels=10)是把计算得到的密度值的绝对数值范围等分成10份,两者划分逻辑完全不同,最终呈现的色块分布自然差异很大。 - 带宽计算存在潜在差异:
scipy.stats.gaussian_kde和Seaborn内部的KDE实现虽然默认都采用Scott带宽规则,但Seaborn会对两个维度分别做标准化后计算带宽,和scipy直接基于原始二维数据协方差矩阵计算带宽的逻辑存在细微差别,会导致密度分布的平滑程度有差异。 - 色阶映射适配逻辑不同:
sns.kdeplot默认会将最低密度值对应色阶的最浅端、最高密度对应最深端,而plt.contourf会根据传入的层级自动适配色阶,因为层级划分逻辑不同,最终的明暗呈现也会有区别。
修正方案
你可以调整contourf的层级计算逻辑,对齐Seaborn的分位数规则,就能得到和sns.kdeplot几乎一致的结果,修正后的代码如下:
import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy import stats # 原代码缺了该导入,直接运行会报错 geyser = sns.load_dataset("geyser") dur = np.array(geyser["duration"]) wait = np.array(geyser["waiting"]) # 自定义KDE实现 def make_kde(x, y): kde = stats.gaussian_kde([x, y]) xx, yy = np.mgrid[min(x):max(x):(max(x)-min(x))/200, min(y):max(y):(max(y)-min(y))/200] density = kde(np.c_[xx.flat, yy.flat].T).reshape(xx.shape) return xx, yy, density xgrid, ygrid, density = make_kde(dur, wait) fig, ax = plt.subplots() # 核心修改:用密度的分位数作为层级,对齐Seaborn逻辑 levels = np.quantile(density, np.linspace(0, 0.9, 10)) ax.contourf(xgrid, ygrid, density, cmap="binary", levels=levels) plt.show()
原代码对应效果参考
- sns.kdeplot生成效果:

- 原plt.contourf生成效果:

内容的提问来源于stack exchange,提问作者sdg。
相关产品推荐
相关产品推荐

