Un estimador de densidad de kernel (KDE) produce una distribución que es una mezcla de ubicación de la distribución de kernel, por lo que para dibujar un valor de la estimación de densidad de kernel, todo lo que necesita hacer es (1) dibujar un valor de la densidad de kernel y luego (2) seleccione independientemente uno de los puntos de datos al azar y agregue su valor al resultado de (1).
Aquí está el resultado de este procedimiento aplicado a un conjunto de datos como el de la pregunta.
El histograma a la izquierda representa la muestra. Como referencia, la curva negra traza la densidad de la que se extrajo la muestra. La curva roja traza el KDE de la muestra (usando un ancho de banda estrecho). (No es un problema, ni siquiera inesperado, que los picos rojos sean más cortos que los negros: el KDE extiende las cosas, por lo que los picos se reducirán para compensar).
El histograma de la derecha muestra una muestra (del mismo tamaño) del KDE. Las curvas negras y rojas son las mismas que antes.
Evidentemente, el procedimiento utilizado para tomar muestras de la densidad funciona. También es extremadamente rápido: la R
implementación a continuación genera millones de valores por segundo desde cualquier KDE. Lo he comentado mucho para ayudar a portar a Python u otros idiomas. El algoritmo de muestreo en sí se implementa en la función rdens
con las líneas.
rkernel <- function(n) rnorm(n, sd=width)
sample(x, n, replace=TRUE) + rkernel(n)
rkernel
dibuja n
muestras iid de la función del kernel mientras sample
dibuja n
muestras con reemplazo de los datos x
. El operador "+" agrega las dos matrices de muestras componente por componente.
Para aquellos que desean una demostración formal de corrección, la ofrezco aquí. Deje que represente la distribución del núcleo con CDF y deje que los datos sean . Según la definición de una estimación del núcleo, el CDF de KDE esF K x = ( x 1 , x 2 , … , x n )KFKx=(x1,x2,…,xn)
Fx^;K(x)=1n∑i=1nFK(x−xi).
La receta anterior dice que se extrae de la distribución empírica de los datos (es decir, se alcanza el valor con probabilidad para cada ), se extrae independientemente una variable aleatoria de la distribución del núcleo y se suma. Te debo una prueba de que la función de distribución de es la de KDE. Comencemos con la definición y veamos a dónde lleva. Deje ser cualquier número real. Condicionamiento en dax i 1 / n i Y X + Y x XXxi1/niYX+YxX
FX+Y(x)=Pr(X+Y≤x)=∑i=1nPr(X+Y≤x∣X=xi)Pr(X=xi)=∑i=1nPr(xi+Y≤x)1n=1n∑i=1nPr(Y≤x−xi)=1n∑i=1nFK(x−xi)=Fx^;K(x),
según lo reclamado.
#
# 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))