什么是核密度估算图(KDE图)?

maxresdefault-3

理解数据分布是数据分析最重要的方面之一。可视化分布有助于我们理解可能隐藏在原始数字中的模式、趋势和异常。虽然直方图通常用于此目的,但它们有时可能过于块状,无法显示一些细微的细节。核密度估计 (KDE) 图通过估计连续数据的概率密度函数,提供了一种更平滑、更准确的可视化方法。这使得数据科学家和分析师能够更清晰地看到重要的特征,例如多个峰值、偏度和异常值。学习使用 KDE 图是更好地理解数据洞察的宝贵技能。在本文中,我们将介绍 KDE 图及其实现。

什么是核密度估计 (KDE) 图?

核密度估计 (KDE) 是一种非参数方法,用于估计连续随机变量的概率密度函数 (PDF)。简而言之,KDE 会生成一条平滑的曲线(密度估计值),以近似数据的分布,而不是像直方图那样使用分隔的区间。从概念上讲,我们在每个数据点上都有一个“核”(一个平滑且对称的函数),并将它们相加以形成连续的密度。从数学上讲,如果我们有数据点 x1,…,xn,那么点 x 处的 KDE 为:

核密度估计 (KDE)

其中 K 是核函数(通常是钟形函数),h 是带宽(平滑参数)。由于分布没有像“正态”或“指数”这样的固定形式,因此 KDE 被称为非参数估计器。KDE 通过将每个数据点变成一座小山丘来“平滑直方图”;所有这些小山丘加在一起构成了总密度(如下图所示)。

平滑直方图

根据用例,会使用不同类型的核函数。例如,高斯(或正态)核因其平滑度而广受欢迎,但也可以使用其他核函数,例如 Epanechnikov(抛物线)、均匀、三角形、双权重甚至三权重。默认情况下,许多库都使用高斯核,这意味着每个数据点都会使估计值呈现钟形凸起。Epanechnikov 核可以最小化所有数据点之间的均方误差,但人们通常仍然选择高斯核只是为了方便。

密度图在分析数据以显示分布形状方面非常有用。它们适用于大数据集,并且可以显示直方图可能隐藏的内容(例如多个峰值或长尾)。例如,KDE 图可以捕捉双峰或倾斜形状,从而告知您有关子组或异常值的信息。在探索新的数值变量时,绘制 KDE 通常是人们首先要做的事情之一。在某些领域(如信号处理或计量经济学),KDE 也称为 Parzen-Rosenblatt 窗口方法。

重要概念

理解 KDE 图的工作原理时,需要牢记以下关键点:

  • 非参数 PDF 估计:KDE 不假设底层分布。它直接根据数据构建平滑的估计值。
  • 核函数:核函数 K(例如高斯核)是一个对称加权函数。常见的核函数包括高斯核、Epanechnikov 核、均匀核等。只要调整带宽,核函数的选择对结果的影响很小。
  • 带宽(平滑):参数 h(或等效地,bw)会缩放核函数。h 越大,曲线越平滑(越宽);h 越小,曲线越紧密、越详细。最佳带宽通常缩放比例为 n−1/5。
  • 偏差-方差权衡:一个关键考虑因素是在细节和平滑度之间取得平衡:h 太小会导致估计值有噪声;太大的 h 可能会使重要的峰或谷变得过于平滑。

在Python中使用KDE绘图

Seaborn(基于 Matplotlib 构建)和 Pandas 都可以轻松地在 Python 中创建 KDE 绘图。现在,我将展示一些使用模式、参数和自定义技巧。

Seaborn的kdeplot

首先,使用 seaborn.kdeplot 函数。该函数为数据集绘制单变量(或双变量)KDE 曲线。它内部默认使用高斯核,并支持许多其他选项。例如,绘制鸢尾花数据集中 sepal_width 变量的分布。

使用 Seaborn 绘制单变量 KDE 绘图(Iris 数据集示例)

以下示例演示如何为单个连续变量创建 KDE 绘图。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import seaborn as sns
import matplotlib.pyplot as plt
# Load example dataset
df = sns.load_dataset('iris')
# Plot 1D KDE
sns.kdeplot(data=df, x='sepal_width', fill=True)
plt.title("KDE of Iris Sepal Width")
plt.xlabel("Sepal Width")
plt.ylabel("Density")
plt.show()
import seaborn as sns import matplotlib.pyplot as plt # Load example dataset df = sns.load_dataset('iris') # Plot 1D KDE sns.kdeplot(data=df, x='sepal_width', fill=True) plt.title("KDE of Iris Sepal Width") plt.xlabel("Sepal Width") plt.ylabel("Density") plt.show()
import seaborn as sns
import matplotlib.pyplot as plt
# Load example dataset
df = sns.load_dataset('iris')
# Plot 1D KDE
sns.kdeplot(data=df, x='sepal_width', fill=True)
plt.title("KDE of Iris Sepal Width")
plt.xlabel("Sepal Width")
plt.ylabel("Density")
plt.show()

单变量 KDE 绘图

从上图中,我们可以看到 speal_width 值的密度曲线平滑。此外,fill=True 参数会塑造曲线下方的面积,如果 fill = False,则只能看到深蓝色的线。

比较不同类别的 KDE 图

到目前为止,我们已经了解了简单的单变量 KDE 图。现在,让我们看看 Seaborn 的 kdeplot 方法最强大的用途之一,即它能够使用 hue 参数比较不同子组的分布。

假设我们想要分析午餐和晚餐时段餐厅总账单的分布差异。因此,为此,我们将使用 tips 数据集。这样,我们可以在同一轴上叠加两个 KDE 图(一个用于午餐,一个用于晚餐),以便直接比较。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
sns.kdeplot(data=tips, x='total_bill', hue='time', fill=True,
common_norm=False, alpha=0.5)
plt.title("KDE of Total Bill (Lunch vs Dinner)")
plt.show()
import seaborn as sns import matplotlib.pyplot as plt tips = sns.load_dataset('tips') sns.kdeplot(data=tips, x='total_bill', hue='time', fill=True, common_norm=False, alpha=0.5) plt.title("KDE of Total Bill (Lunch vs Dinner)") plt.show()
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
sns.kdeplot(data=tips, x='total_bill', hue='time', fill=True,
common_norm=False, alpha=0.5)
plt.title("KDE of Total Bill (Lunch vs Dinner)")
plt.show()

叠加两个 KDE 图

因此,我们可以看到上面的代码叠加了两条密度曲线。fill=True 会在每条曲线下方添加阴影,使差异更加明显;common_norm=False 确保每个组的密度值独立缩放; alpha=0.5 则增加了透明度,使重叠区域更容易理解。

您还可以尝试使用 multiple=‘layer’、‘stack’ 或 ‘fill’ 来更改多重密度图的显示方式。

Pandas和Matplotlib

如果您使用 Pandas,还可以使用内置绘图功能来获取 KDE 图。Pandas 系列数据包含 plot(kind='density')  或 plot.density() 方法,它们充当 Matplotlib 中相关方法的包装器。

代码:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import pandas as pd
import numpy as np
data = np.random.randn(1000) # 1000 random points from a normal distribution
s = pd.Series(data)
s.plot(kind='density')
plt.title("Pandas Density Plot")
plt.xlabel("Value")
plt.show()
import pandas as pd import numpy as np data = np.random.randn(1000) # 1000 random points from a normal distribution s = pd.Series(data) s.plot(kind='density') plt.title("Pandas Density Plot") plt.xlabel("Value") plt.show()
import pandas as pd
import numpy as np
data = np.random.randn(1000) # 1000 random points from a normal distribution
s = pd.Series(data)
s.plot(kind='density')
plt.title("Pandas Density Plot")
plt.xlabel("Value")
plt.show()

Pandas 绘制 KDE 图

或者,我们可以使用 SciPy 的 gaussian_kde 方法手动计算和绘制 KDE。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import numpy as np
from scipy.stats import gaussian_kde
data = np.concatenate([np.random.normal(-2, 0.5, 300), np.random.normal(3,
1.0, 500)])
kde = gaussian_kde(data, bw_method=0.3) # bandwidth can be a factor or
'silverman', 'scott'
xs = np.linspace(min(data), max(data), 200)
density = kde(xs)
plt.plot(xs, density)
plt.title("Manual KDE via scipy")
plt.xlabel("Value"); plt.ylabel("Density")
plt.show()
import numpy as np from scipy.stats import gaussian_kde data = np.concatenate([np.random.normal(-2, 0.5, 300), np.random.normal(3, 1.0, 500)]) kde = gaussian_kde(data, bw_method=0.3) # bandwidth can be a factor or 'silverman', 'scott' xs = np.linspace(min(data), max(data), 200) density = kde(xs) plt.plot(xs, density) plt.title("Manual KDE via scipy") plt.xlabel("Value"); plt.ylabel("Density") plt.show()
import numpy as np
from scipy.stats import gaussian_kde
data = np.concatenate([np.random.normal(-2, 0.5, 300), np.random.normal(3,
1.0, 500)])
kde = gaussian_kde(data, bw_method=0.3) # bandwidth can be a factor or
'silverman', 'scott'
xs = np.linspace(min(data), max(data), 200)
density = kde(xs)
plt.plot(xs, density)
plt.title("Manual KDE via scipy")
plt.xlabel("Value"); plt.ylabel("Density")
plt.show()

手动计算和绘制 KDE

上述代码创建了一个双峰数据集并估算了其密度。实际上,使用 Seaborn 或 Pandas 实现相同功能要容易得多。

解读KDE图或核密度估计图

解读 KDE 图类似于直方图,但 KDE 图的曲线较为平滑。点 x 处的曲线高度与该点的估计概率密度成正比。曲线在一定范围内的面积对应于落在该范围内的概率。由于曲线是连续的,因此任何一点的确切值都不如整体形状重要:

  • 峰(众数):高峰表示数据中存在一个共同值或聚类。多个峰表示存在多个众数(例如,子群体的混合)。
  • 散布:曲线的宽度表示离散程度。曲线越宽表示变异性越大(标准差越大),而曲线越窄、越高则表示数据聚集程度较高。
  • 尾部:观察密度减小的速度。粗尾表示存在异常值;短尾表示数据有界。
  • 比较曲线:当叠加组时,寻找变化(一个分布系统地更高或更低)或形状的差异。

用例和示例

KDE 图在日常数据分析中有很多实用的应用:

  • 探索性数据分析 (EDA):当我们首次查看数据集时,KDE 可以帮助我们了解变量的分布情况,无论它们是正态分布、偏态分布还是具有多个峰值(多峰)。众所周知,逐个检查变量的分布可能是获得新数据集后应该做的第一件事。KDE 比直方图更平滑,因此在 EDA 过程中了解数据时通常更有帮助。
  • 比较分布:当我们想要比较不同群体的表现时,KDE 非常有效。例如,在同一轴上绘制男孩和女孩测试分数的 KDE 值,可以显示平均值或方差是否存在差异。Seaborn 可以非常轻松地使用不同颜色叠加 KDE。KDE 图通常比并排的直方图更简洁,并且能够更好地展现不同群体之间的差异。
  • 平滑直方图:KDE 可以被认为是直方图的平滑版本。当直方图看起来过于波动或随 bin 大小变化很大时,KDE 会提供更稳定、更清晰的图像。例如,上面的 Airbnb 价格示例可以用直方图显示,但 KDE 使其更容易解读。KDE 有助于对数据形状进行更连续的估计,这非常方便,尤其是在数据量适中的情况下。

核密度图的替代方案

虽然核密度图对于显示分布的平滑估计非常有用,但它并非总是最佳选择。根据数据大小或具体目标,您还可以尝试其他类型的图。以下是一些常见的图:

直方图

说实话,这是观察分布最基本的方法。你只需将数据切分成几个箱,然后计算每个箱中有多少数据。使用起来很简单,但如果使用的箱数太多或太少,就会变得混乱。有时它会隐藏一些规律。KDE 可以通过平滑这些起伏来解决这个问题。

直方图

箱线图(也称为盒须图)

如果您只是想知道大多数数据的位置,例如中位数、四分位数等,那么箱线图就很有用。它可以快速识别异常值。但它不像 KDE 那样能够真正展现数据的形状。如果您不需要所有细节,它仍然很有用。

箱线图(也称为盒须图)

小提琴图

你可以把它们想象成箱线图的升级版,同时还能展示KDE形状。它兼具两者的优点,既能提供汇总统计数据,又能直观地了解分布情况。我用它来并排比较不同组的数据。

小提琴图

地毯图

地毯图很简单。它们只是将每个数据点显示为轴上的小垂直线。通常与 KDE 配合使用,以显示实际数据点的位置。但是,当数据量过大时,地毯图看起来可能会有些混乱。

地毯图

直方图 + KDE 组合

有些人喜欢将直方图与 KDE 结合使用,因为直方图显示计数,而 KDE 在顶部添加一条平滑曲线。这样,他们就可以同时看到原始频率和平滑后的模式。

直方图 + KDE 组合

说实话,使用哪种方式取决于你的需求。KDE 非常适合平滑模式,但有时你并不需要所有这些;也许一个简单的箱线图或直方图就足够了,尤其是在你时间紧迫或只是快速浏览内容的情况下。

小结

KDE 图提供了一种强大且直观的方式来可视化连续数据的分布。与普通的直方图不同,它们通过核函数估计概率密度函数,从而绘制出平滑连续的曲线,这使得诸如偏度、多模态或异常值等细微模式更容易被注意到。无论您是在进行探索性数据分析、比较分布还是发现异常,KDE 图都能为您提供极大的帮助。Seaborn 或 Pandas 等工具使创建和使用 KDE 图变得非常简单。

评论留言