在数据可视化中,直方图是一种常用的方法来展示一维数据的分布情况。然而,直方图的一个问题是它依赖于对数据的网格划分,不同的网格划分方式可能导致对数据分布形状的误解。例如,如果将每个数据点上方放置一个单位“块”,如顶部两个面板所示,网格的选择可能导致对密度分布形状的截然不同的理解。
为了解决这个问题,可以将每个块的中心放在它所代表的点上,如左下角面板所示。这是一种使用“顶帽”核的核密度估计。这个想法可以推广到其他核形状:第一个图的右下角面板显示了在同一分布上使用高斯核的核密度估计。
Scikit-learn库通过使用Ball Tree或KD Tree结构,实现了高效的核密度估计,通过KernelDensity估计器。第二个图展示了可用的核形状。第三个图比较了一维分布的100个样本的核密度估计。尽管这个示例使用了一维分布,但核密度估计也可以轻松且高效地扩展到更高维度。
以下是一个使用Python和Scikit-learn库实现核密度估计的示例代码。这段代码首先生成了一些随机数据,然后使用不同的核函数来估计这些数据的密度分布,并绘制出相应的图形。
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
from sklearn.neighbors import KernelDensity
# 设置随机种子以保证结果的可重复性
np.random.seed(1)
# 生成一些随机数据
N = 100
X = np.concatenate((np.random.normal(0, 1, int(0.3 * N)), np.random.normal(5, 1, int(0.7 * N))))[:, np.newaxis]
X_plot = np.linspace(-5, 10, 1000)[:, np.newaxis]
# 绘制直方图和核密度估计图
fig, ax = plt.subplots(2, 2, sharex=True, sharey=True)
fig.subplots_adjust(hspace=0.05, wspace=0.05)
# 绘制直方图
ax[0, 0].hist(X[:, 0], bins=np.linspace(-5, 10, 10), fc="#AAAAFF", density=True)
ax[0, 0].text(-3.5, 0.31, "直方图")
# 绘制核密度估计图
kde = KernelDensity(kernel="tophat", bandwidth=0.75).fit(X)
log_dens = kde.score_samples(X_plot)
ax[1, 0].fill(X_plot[:, 0], np.exp(log_dens), fc="#AAAAFF")
ax[1, 0].text(-3.5, 0.31, "顶帽核密度")
# 绘制高斯核密度估计图
kde = KernelDensity(kernel="gaussian", bandwidth=0.75).fit(X)
log_dens = kde.score_samples(X_plot)
ax[1, 1].fill(X_plot[:, 0], np.exp(log_dens), fc="#AAAAFF")
ax[1, 1].text(-3.5, 0.31, "高斯核密度")
# 绘制所有可用的核形状
X_plot = np.linspace(-6, 6, 1000)[:, None]
X_src = np.zeros((1, 1))
fig, ax = plt.subplots(2, 3, sharex=True, sharey=True)
fig.subplots_adjust(left=0.05, right=0.95, hspace=0.05, wspace=0.05)
for i, kernel in enumerate(["gaussian", "tophat", "epanechnikov", "exponential", "linear", "cosine"]):
axi = ax.ravel()[i]
log_dens = KernelDensity(kernel=kernel).fit(X_src).score_samples(X_plot)
axi.fill(X_plot[:, 0], np.exp(log_dens), "-k", fc="#AAAAFF")
axi.text(-2.6, 0.95, kernel)
# 绘制一维密度示例
N = 100
np.random.seed(1)
X = np.concatenate((np.random.normal(0, 1, int(0.3 * N)), np.random.normal(5, 1, int(0.7 * N))))[:, np.newaxis]
X_plot = np.linspace(-5, 10, 1000)[:, np.newaxis]
true_dens = 0.3 * norm(0, 1).pdf(X_plot[:, 0]) + 0.7 * norm(5, 1).pdf(X_plot[:, 0])
fig, ax = plt.subplots()
ax.fill(X_plot[:, 0], true_dens, fc="black", alpha=0.2, label="输入分布")
colors = ["navy", "cornflowerblue", "darkorange"]
kernels = ["gaussian", "tophat", "epanechnikov"]
lw = 2
for color, kernel in zip(colors, kernels):
kde = KernelDensity(kernel=kernel, bandwidth=0.5).fit(X)
log_dens = kde.score_samples(X_plot)
ax.plot(X_plot[:, 0], np.exp(log_dens), color=color, lw=lw, linestyle="-", label="核 = '%s'" % kernel)
ax.text(6, 0.38, "N=%d点" % N)
ax.legend(loc="upper left")
ax.plot(X[:, 0], -0.005 - 0.01 * np.random.random(X.shape[0]), "+k")
ax.set_xlim(-4, 9)
ax.set_ylim(-0.02, 0.4)
plt.show()
这段代码首先导入了必要的库,然后生成了一些随机数据。接着,它使用不同的核函数来估计这些数据的密度分布,并绘制出相应的图形。这些图形包括直方图、顶帽核密度估计图和高斯核密度估计图。此外,代码还展示了所有可用的核形状,并绘制了一个一维密度示例。
通过这个示例,可以看到核密度估计是一种强大的工具,可以帮助更好地理解数据的分布情况。与传统的直方图相比,核密度估计可以提供更平滑、更连续的分布估计,从而帮助更准确地分析数据。
核密度估计的原理是基于概率密度函数的概念。在核密度估计中,使用一个核函数来估计数据点的概率密度。核函数通常是一个平滑的函数,它可以将数据点的影响扩散到周围的区域。通过这种方式,可以估计出整个数据集的概率密度分布。
在实际应用中,核密度估计可以用于许多不同的领域,包括统计分析、机器学习、图像处理等。例如,在机器学习中,核密度估计可以用于特征选择和异常检测。在图像处理中,核密度估计可以用于图像去噪和边缘检测。