周期数据的核密度估计

3

sklearn 或其他 Python 包中是否可以获得针对 周期性 数据的 KDE?

以下是一个简化示例:我从两个正态分布中创建了一个模拟数据集,并将其映射到从0到20的区间。

import numpy as np
import matplotlib.pyplot as plt

# create dataset
data = np.hstack((np.random.normal(8, 2, 200), np.random.normal(19, 4, 200))) % 20

当我绘制KDE的结果时

# fit
from sklearn.neighbors import KernelDensity
kde = KernelDensity(bandwidth=1, kernel='gaussian')
kde.fit(data[:, None])

# plot
x_d = np.linspace(0, 20, 100)
logprob = kde.score_samples(x_d[:, None])
plt.fill_between(x_d, np.exp(logprob), alpha=0.5)
plt.hist(data, histtype = "step", density = True)
plt.ion()
plt.show()

显然,IT不知道数据的周期性:

KDE result

从估计结果可以看出,有三个峰值并且在边界处不光滑。

1个回答

2

我自己一直在寻找这个,但是发现直接/简单支持这个的软件包似乎很难找到……在此期间,我不得不想出一个变通办法……虽然有点痛苦,但确实可行。

由于你处于一维周期性域(从0到20),因此一种解决方案是将数据映射到高维2D圆形流形上。例如,每个数据点都被映射从0-20到0-2pi,然后映射到单位圆上,如x2d = cos(x_r),sin(x_r)。在那一点上,您可以在更高的维度空间中运行kde以获得密度,然后对流形进行采样并归一化为概率/密度。

最后要注意的是,如果您想将此应用于纬度/经度数据,则您的流形将是单位球,因此您需要相应地进行转换。

scaleFactor=2*np.pi/20. #need to rescale from 0-20 to 0-2pi
data2d=np.array(
    [
        np.cos(data*scaleFactor),
        np.sin(data*scaleFactor)
    ]
).T

#also need to scale the bandwidth appropriately (if you have one picked)
kde2d = KernelDensity(bandwidth=1*scaleFactor, kernel='gaussian')
kde2d.fit(data2d)

x_d=np.linspace(0,20,100)
x_2d=np.array(
    [
        np.cos(x_d*scaleFactor),
        np.sin(x_d*scaleFactor)
    ]
).T

logprob2d = kde2d.score_samples(x_2d)
prob2d=np.exp(logprob2d)
prob2d=prob2d/np.sum(prob2d)
densFactor=len(prob2d)/20. #convert from probability to density
dens2d=prob2d*densFactor
plt.fill_between(
    x_d, 
    dens2d,
    alpha=0.5)
plt.hist(data, histtype = "step", density = True)
plt.ion()
plt.show()

#verify that prob2d is now periodic
print(prob2d[0],prob2d[-1])

还有另一种选择……创建数据的偏移副本并将它们附加(就像在“中央单元格”周围添加周期性图像一样)……尽管这可能需要大量内存和时间。在更高的维度中,您最终需要(3^N)-1个副本。这也会使缩放成为一个严重的问题,因为许多KDE方法在某一点后开始与样本大小非线性地缩放。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接