#python #scikit-learn #kernel-density
#python #scikit-learn #плотность ядра
Вопрос:
Возможно ли получить KDE для периодических данных в sklearn
или в другом пакете Python?
Вот упрощенный пример: я создаю макет набора данных из двух нормальных распределений и сопоставляю его с интервалом от 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()
он (очевидно) не знает о периодичности данных:
как вы можете видеть из оценки, имеющей три пика и не являющейся гладкой на границах.
Ответ №1:
Я сам искал это, хотя найти пакет, который напрямую / просто поддерживает это, кажется на удивление сложным… В то же время мне пришлось придумать обходной путь… это немного болезненно, но это работает.
Поскольку вы находитесь в одномерной периодической области (от 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 начинают масштабироваться нелинейно по отношению к размеру выборки после определенной точки.