大家好,我是你们的小帅学长。
在上一篇里,我们讲了直方图(Histogram),也讲了一个关键问题:bin 怎么选?但你可能已经发现一个现象:同一组数据,只要改变分箱数,直方图的形状就可能明显变化。
有时候,bin 少了 → 结构被抹平;bin 多了 → 噪声变结构。这时候很多人就会问,有没有一种方法,不需要分箱,也能看分布?
答案就是今天的主角:KDE(Kernel Density Estimation)核密度估计。简单理解为,KDE 就是一条“平滑版的直方图”。
01.KDE 到底在做什么?
直方图的逻辑是,把数据切成很多小箱子,然后统计每个箱子的数量。
而 KDE 的逻辑是,在每个数据点上放一个小“平滑曲线”,再把它们加起来。最终得到一条连续的概率密度曲线。
所以 KDE 的核心特点是 没有分箱边界、曲线连续、更容易看出整体趋势。
很多论文里的“分布曲线图”,其实就是 KDE。
02. 什么时候 KDE 比直方图更好?
1)当你关心的是“整体形态”
例如,是否单峰 / 多峰?是否偏态?分布是否平滑?这时候,KDE 会比直方图更直观。
特别是在数据量较大的情况下(n > 200),KDE 往往能更好地呈现真实结构。
2)当你需要比较多组分布
如果你想比较:模型 A vs 模型 B、男 vs 女、城市 vs 农村,这时候,直方图叠加往往会很乱。
而 KDE 可以这样画:两条平滑曲线叠在一起,趋势差异一眼可见。
3)当你不希望 bin 影响结果
直方图的一个问题是:分箱边界会影响形状。
KDE 不存在这个问题,因为它没有 bin。
03. KDE 的关键参数:bandwidth(带宽)
如果说直方图的核心是 bin,那 KDE 的核心就是 bandwidth。
带宽控制的是 曲线有多“平滑”。
带宽太小:曲线会非常锯齿、就像把噪声当结构
带宽太大:曲线会过度平滑、多个峰可能被合并
所以,KDE 的艺术,其实是带宽选择。
好消息是 大多数软件已经有不错的自动选择规则。
例如:
Scott rule
Silverman rule
通常已经足够。
04. KDE 的常见误区
1)KDE 一定比直方图好
不是。如果样本量很小(n < 50),KDE 可能会画出“看起来很平滑但不真实”的曲线。这时候直方图反而更可靠。
2)KDE 是概率
很多人误以为曲线高度就是概率。
实际上,KDE 表示的是概率密度。曲线下面积才是概率。
3)KDE 可以解释每个细节
KDE 是一种估计方法。曲线形状会受带宽影响,所以不要过度解读细节。
05. Python 示例代码(直方图 + KDE)
import osimport numpy as npimport matplotlib as mplimport matplotlib.pyplot as pltimport seaborn as snsfrom matplotlib import font_manager as fmfrom matplotlib.ticker import MaxNLocatorwin_fonts = r"C:\Windows\Fonts"for p in [ os.path.join(win_fonts, "times.ttf"), os.path.join(win_fonts, "timesbd.ttf"), os.path.join(win_fonts, "timesi.ttf"), os.path.join(win_fonts, "simsun.ttc"),]: if os.path.exists(p): try: fm.fontManager.addfont(p) except Exception: passmpl.rcParams["font.family"] = ["Times New Roman", "SimSun"]mpl.rcParams["axes.unicode_minus"] = FalseOUT_DIR = r"D:\py_figs"os.makedirs(OUT_DIR, exist_ok=True)np.random.seed(42)data_A = np.concatenate([ np.random.normal(0, 1, 400), np.random.normal(4, 1.2, 400)])data_B = np.random.normal(2, 1.5, 800)mean_A = np.mean(data_A)median_A = np.median(data_A)mean_B = np.mean(data_B)median_B = np.median(data_B)fig, ax = plt.subplots(figsize=(8,5))# 直方图sns.histplot( data_A, bins="fd", stat="density", alpha=0.35, ax=ax)# KDE曲线sns.kdeplot( data_A, linewidth=2.5, label="Distribution A")sns.kdeplot( data_B, linewidth=2.5, linestyle="--", label="Distribution B")# 均值线ax.axvline(mean_A, linestyle="--", linewidth=1.5)ax.axvline(mean_B, linestyle="--", linewidth=1.5)# 中位数线ax.axvline(median_A, linestyle="-.", linewidth=1.5)ax.axvline(median_B, linestyle="-.", linewidth=1.5)ax.set_title("Histogram + KDE Density Comparison / 直方图与KDE密度对比", fontsize=14)ax.set_xlabel("Value / 数值", fontsize=12)ax.set_ylabel("Density / 密度", fontsize=12)ax.xaxis.set_major_locator(MaxNLocator(nbins=6))ax.yaxis.set_major_locator(MaxNLocator(nbins=6))ax.legend(frameon=False)out_path = os.path.join(OUT_DIR, "kde_density_demo.jpg")fig.savefig( out_path, dpi=300, bbox_inches="tight", pad_inches=0.05)plt.close()print("Saved:", out_path)

在这张图中,直方图展示的是样本频率分布,而 KDE 曲线展示的是平滑的概率密度。虚线表示均值(mean),点线表示中位数(median),通过这些辅助信息可以更清楚地理解分布结构。
06. 什么时候该用 KDE?
你可以记住一个简单原则:
看完分布之后,我们接下来要讲一个统计含义非常强的图:《箱线图(解释统计含义 + 别误读)》。箱线图不仅能看分布,还能直接读出中位数、四分位距、异常值,但很多人其实一直在误读箱线图。下一篇,我会把箱线图背后的统计逻辑讲清楚。
——期待你的关注——
往期内容:
用Python做科研级画图——哑铃图
用Python做科研级画图——直方图
用Python做科研级画图——点图替代柱状
用Python做科研级画图——分组对比
用Python做科研级画图——柱状图基础