Оценка плотности ядра для периодических данных

#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()
 

он (очевидно) не знает о периодичности данных:

Результат KDE

как вы можете видеть из оценки, имеющей три пика и не являющейся гладкой на границах.

Ответ №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 начинают масштабироваться нелинейно по отношению к размеру выборки после определенной точки.