Оценщик плотности ядра (KDE) создает распределение, представляющее собой смесь локаций распределения ядра, поэтому для получения значения из оценки плотности ядра все, что вам нужно сделать, это (1) извлечь значение из плотности ядра, а затем (2) независимо выберите случайным образом одну из точек данных и добавьте ее значение к результату (1).
Вот результат этой процедуры, примененной к набору данных, подобному тому, который указан в вопросе.
Гистограмма слева изображает образец. Для справки, черная кривая показывает плотность, из которой был взят образец. Красная кривая отображает KDE образца (с использованием узкой полосы пропускания). (Это не проблема или даже неожиданность, что красные пики короче, чем черные пики: KDE распределяет вещи, поэтому пики будут уменьшаться для компенсации.)
Гистограмма справа показывает образец (того же размера) из KDE. Черные и красные кривые такие же, как и раньше.
Очевидно, процедура, используемая для отбора проб из плотины работает. Это также чрезвычайно быстро: приведенная R
ниже реализация генерирует миллионы значений в секунду из любого KDE. Я прокомментировал это сильно, чтобы помочь в портировании на Python или другие языки. Сам алгоритм выборки реализован в функции rdens
со строками
rkernel <- function(n) rnorm(n, sd=width)
sample(x, n, replace=TRUE) + rkernel(n)
rkernel
рисует n
образцы из функции ядра, а sample
рисует n
образцы с заменой из данных x
. Оператор «+» добавляет два массива выборок компонент за компонентом.
КFКх =( х1, х2, … , ХN)
FИкс^;К( х ) = 1NΣя = 1NFК( х - хя) .
ИксИкся1 / nяYИкс+ YИксИкс
FИкс+ Y( х )= Pr ( X+ Y≤ х )= ∑я = 1NPr ( X+ Y≤ x ∣ X= хя) Pr ( X= хя)= ∑я = 1NPr ( xя+ Y≤ х ) 1N= 1NΣя = 1NPr ( Y≤ х - хя)= 1NΣя = 1NFК( х - хя)= FИкс^;К( х ) ,
как утверждено.
#
# Define a function to sample from the density.
# This one implements only a Gaussian kernel.
#
rdens <- function(n, density=z, data=x, kernel="gaussian") {
width <- z$bw # Kernel width
rkernel <- function(n) rnorm(n, sd=width) # Kernel sampler
sample(x, n, replace=TRUE) + rkernel(n) # Here's the entire algorithm
}
#
# Create data.
# `dx` is the density function, used later for plotting.
#
n <- 100
set.seed(17)
x <- c(rnorm(n), rnorm(n, 4, 1/4), rnorm(n, 8, 1/4))
dx <- function(x) (dnorm(x) + dnorm(x, 4, 1/4) + dnorm(x, 8, 1/4))/3
#
# Compute a kernel density estimate.
# It returns a kernel width in $bw as well as $x and $y vectors for plotting.
#
z <- density(x, bw=0.15, kernel="gaussian")
#
# Sample from the KDE.
#
system.time(y <- rdens(3*n, z, x)) # Millions per second
#
# Plot the sample.
#
h.density <- hist(y, breaks=60, plot=FALSE)
#
# Plot the KDE for comparison.
#
h.sample <- hist(x, breaks=h.density$breaks, plot=FALSE)
#
# Display the plots side by side.
#
histograms <- list(Sample=h.sample, Density=h.density)
y.max <- max(h.density$density) * 1.25
par(mfrow=c(1,2))
for (s in names(histograms)) {
h <- histograms[[s]]
plot(h, freq=FALSE, ylim=c(0, y.max), col="#f0f0f0", border="Gray",
main=paste("Histogram of", s))
curve(dx(x), add=TRUE, col="Black", lwd=2, n=501) # Underlying distribution
lines(z$x, z$y, col="Red", lwd=2) # KDE of data
}
par(mfrow=c(1,1))