是否有可能在 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()
它(显然)不知道数据的周期性:
正如您从估计中看到的那样具有三个峰值并且在边界处不平滑。
最佳答案
我自己一直在寻找这个,虽然找到一个直接/简单地支持它的包似乎出奇地困难......同时我不得不想出一个解决方法......它有点痛苦但它有效.
由于您处于一维周期域(从 0 到 20),一种解决方案是将数据映射到更高维度的二维圆形流形。例如。每个数据点从 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 方法在某个点后开始相对于样本大小进行非线性缩放。
关于python - 周期数据的核密度估计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65060491/