Un estimateur de densité de noyau (KDE) produit une distribution qui est un mélange d'emplacements de la distribution de noyau, donc pour tirer une valeur de l'estimation de densité de noyau, tout ce que vous devez faire est (1) de tirer une valeur de la densité de noyau, puis (2) sélectionnez indépendamment l'un des points de données au hasard et ajoutez sa valeur au résultat de (1).
Voici le résultat de cette procédure appliquée à un ensemble de données comme celui de la question.
L'histogramme à gauche représente l'échantillon. Pour référence, la courbe noire représente la densité à partir de laquelle l'échantillon a été prélevé. La courbe rouge trace le KDE de l'échantillon (en utilisant une bande passante étroite). (Ce n'est pas un problème, ni même inattendu, que les pics rouges sont plus courts que les pics noirs: le KDE répartit les choses, donc les pics s'abaissent pour compenser.)
L'histogramme à droite représente un échantillon (de la même taille) du KDE. Les courbes noires et rouges sont les mêmes qu'avant.
De toute évidence, la procédure utilisée pour échantillonner à partir de la densité fonctionne. C'est aussi extrêmement rapide: l' R
implémentation ci-dessous génère des millions de valeurs par seconde à partir de n'importe quel KDE. Je l'ai fortement commenté pour aider au portage vers Python ou d'autres langages. L'algorithme d'échantillonnage lui-même est implémenté dans la fonction rdens
avec les lignes
rkernel <- function(n) rnorm(n, sd=width)
sample(x, n, replace=TRUE) + rkernel(n)
rkernel
dessine des n
échantillons iid à partir de la fonction noyau tandis que sample
dessine des n
échantillons avec remplacement à partir des données x
. L'opérateur "+" ajoute les deux tableaux d'échantillons composant par composant.
KFKx =( x1, x2, … , Xn)
FX^;K( x ) = 1n∑i = 1nFK( x - xje) .
XXje1 / njeOuiX+ YXX
FX+ Y( x )= Pr ( X+ Y≤ x )= ∑i = 1nPr ( X+ Y≤ x ∣ X= xje) Pr ( X= xje)= ∑i = 1nPr ( xje+ Y≤ x ) 1n= 1n∑i = 1nPr ( Y≤ x - xje)= 1n∑i = 1nFK( x - xje)= FX^;K( x ) ,
comme revendiqué.
#
# 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))