【问题标题】:Multivariate KDE Scipy Stats - what if it's not Gaussian?多元 KDE Scipy Stats - 如果它不是高斯的怎么办?
【发布时间】:2021-06-26 09:45:35
【问题描述】:

我正在使用一些 2D 数据进行平滑处理:

from scipy.stats import gaussian_kde
kde = gaussian_kde(data)

但是如果我的数据不是 Gaussian/tophat/其他选项怎么办?我的在平滑之前看起来更椭圆,所以我真的应该在 x 和 y 中有不同的带宽吗?一个方向的方差高很多,x轴的值也高,感觉一个简单的高斯可能会漏掉什么?

【问题讨论】:

  • 请提供您的数据样本。概率密度函数docs.scipy.org/doc/scipy/reference/generated/… 的高斯核密度估计适用于单峰分布,正如您在文档中看到的那样,双/多峰分布被过度平滑。
  • PS:如果你对scipy Gaussian KDE 不满意,可以试试KDEpy kdepy.readthedocs.io/en/latest/examples.html,让你选择不同的内核函数
  • 数据类似于:X = np.random.normal(size=(100,1), loc=1, scale = 0.01), Y = np.random.normal(size=(100,1), loc=200, scale =100),即 Y 的幅度更大,分布更广,所以我不想要高斯 KDE,更椭圆?
  • 我不知道你到底是什么意思,我会用你的 X 和 Y 发送答案

标签: python scipy kernel-density scipy.stats


【解决方案1】:

这是我通过您定义的XY 得到的。看起来不错。你期待不同的东西吗?

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

def generate(n):
    # generate data
    np.random.seed(42)
    x = np.random.normal(size=n, loc=1, scale=0.01)
    np.random.seed(1)
    y = np.random.normal(size=n, loc=200, scale=100)
    return x, y

x, y = generate(100)
xmin = x.min()
xmax = x.max()
ymin = y.min()
ymax = y.max()

X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([x, y])
kernel = stats.gaussian_kde(values)
Z = np.reshape(kernel(positions).T, X.shape)

fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(np.rot90(Z), cmap=plt.cm.gist_earth_r,
          extent=[xmin, xmax, ymin, ymax],
          aspect='auto', alpha=.75
         )
ax.plot(x, y, 'ko', ms=5)
ax.set_xlim([xmin, xmax])
ax.set_ylim([ymin, ymax])
plt.show()

xy 的分布是高斯分布。 您也可以通过seaborn 进行验证

import pandas as pd
import seaborn as sns
# I pass a DataFrame because passing
# (x,y) alone will be soon deprecated
g = sns.jointplot(data=pd.DataFrame({'x':x, 'y':y}), x='x', y='y')
g.plot_joint(sns.kdeplot, color="r", zorder=0, levels=6)


更新

二维数据的核密度估计沿每个轴单独完成,然后连接在一起。

让我们用我们已经使用的数据集做一个例子。

正如我们在seaborn 联合图中看到的那样,您不仅有估计的 2d-kde,还有xy边际分布(直方图)。

所以,让我们一步一步地估计xy 的密度,然后评估线性空间上的密度

kde_x = sps.gaussian_kde(x)
kde_x_space = np.linspace(x.min(), x.max(), 100)
kde_x_eval = kde_x.evaluate(kde_x_space)
kde_x_eval /= kde_x_eval.sum()

kde_y = sps.gaussian_kde(y)
kde_y_space = np.linspace(y.min(), y.max(), 100)
kde_y_eval = kde_y.evaluate(kde_y_space)
kde_y_eval /= kde_y_eval.sum()

fig, ax = plt.subplots(1, 2, figsize=(12, 4))
ax[0].plot(kde_x_space, kde_x_eval, 'k.')
ax[0].set(title='KDE of x')
ax[1].plot(kde_y_space, kde_y_eval, 'k.')
ax[1].set(title='KDE of y')
plt.show()

所以我们现在有xy 的边际分布。这些是概率密度函数,因此,x 和 y 的联合概率可以看作是独立事件 xy 的交集,因此我们可以将估计的 x 和 y 概率密度乘以 2d 矩阵和在 3d 投影上绘图

# Grid of x and y
X, Y = np.meshgrid(kde_x_space, kde_y_space)
# Grid of probability density
kX, kY = np.meshgrid(kde_x_eval, kde_y_eval)
# Intersection
Z = kX * kY

fig, ax = plt.subplots(
    2, 2, 
    subplot_kw={"projection": "3d"}, 
    figsize=(10, 10))

for i, (elev, anim, title) in enumerate(zip([10, 10, 25, 25], 
                                            [0, -90, 25, -25],
                                            ['y axis', 'x axis', 'view 1', 'view 2']
                                            )):
    # Plot the surface.
    surf = ax.flat[i].plot_surface(X, Y, Z, cmap=plt.cm.gist_earth_r,
                           linewidth=0, antialiased=False, alpha=.75)
    ax.flat[i].scatter(x, y, zs=0, zdir='z', c='k')
    ax.flat[i].set(
        xlabel='x', ylabel='y',
        title=title
    )
    ax.flat[i].view_init(elev=elev, azim=anim)
plt.show()

这是一个非常简单和朴素的方法,但只是为了了解它的工作原理以及为什么 x 和 y 比例对于 2d-KDE 无关紧要。

【讨论】:

  • 是的,看起来不错,谢谢,但我不确定我怎么理解!如果 KDE 在每个数据点上放置宽度 sigma 的高斯分布,然后将它们相加,如果我查看 x 轴,我会说 sigma 大约是 0.001,但如果我查看 y 轴,我'会说它大约是 1。我只是对它必须使用的 sigma 的近似值感到困惑,因为结果看起来确实不错,我只是想确保我不需要先规范化我的数据或其他东西
  • 你可以在这里找到关于 KDE 工作原理以及不同内核函数如何影响估计的非常好的交互式解释mathisonian.github.io/kde
  • @Lizardinablizzard 我添加了有关 2d-KDE 基础知识的更新,解释了为什么 x 和 y 比例无关紧要