Maybe this picture helps. Actually, the "parametric" kernels only give weights and do not strongly influence (as @conjectures says) the shape of the density estimate, in the sense that different kernels often produce very similar density estimates.
The example shows 20 purple realizations of some mixture of normals (the blue density). The grey bell curves indicate the weights that each observation yields for the points at which we want density estimates ($[-4,4]$ in the picture). A kernel density estimate at some point $x$ then simply consists of stacking the weights at that point on top of each other.
In the picture, we see that at $x=-2.2$ (with the second observation slightly jittered to make both bars fully visible), essentially only two observations contribute weights to the estimate at that point (technically, with a normal kernel, all weights are nonzero, but as the normal tails decay quickly, the weights quickly become negligible). These weights are the magenta and green bars.
That the orange and red curve agree confirms that this handmade approach to constructing a KDE just reproduces what R
s density
command does.
Code:
n <- 20
set.seed(2)
# generate data from mixture density
mixture <- rbinom(n,1,.5)
n1 <- sum(mixture)
x <- sort(c(rnorm(n1,-1,.65),rnorm(n-n1,1,.65)))
# plot theoretical density and data
gridpoints = 1024
x_seq <- seq(-4,4,length = gridpoints)
mixture_density <- function(g) .5*dnorm(g,-1,.65)+.5*dnorm(g,1,.65)
plot(x_seq, mixture_density(x_seq), type="l", lwd=2, col="lightblue", ylab="density", xlab="x")
points(x,rep(0,n),col="purple",cex=1.5,lwd=2)
legend('topright', c("true","handmade","canned","weights","observations"), lty=c(rep(1,4),NA), col=c("lightblue","orange","red","grey","purple"),lwd=c(rep(2,3),1,2), pch = c(rep(NA, 4), 1),bg="white")
Bandwidth <- .3 # play around here
# compute weights arising from individual observations
GaussianWeights <- matrix(nrow=n,ncol=length(x_seq))
for (i in 1:n) {
psi <- (x_seq-x[i])/Bandwidth
GaussianWeights[i,] <- dnorm(psi)/(n*Bandwidth)
lines(x_seq,GaussianWeights[i,],col="grey")
segments(x[i],0,x[i],dnorm(0)/(n*Bandwidth),lty=3,col="grey")
}
segments(-2.2,0,-2.2,dnorm((-2.2-x[2])/Bandwidth)/(n*Bandwidth),col="green",lwd=2)
segments(-2.22,0,-2.22,dnorm((-2.22-x[1])/Bandwidth)/(n*Bandwidth),col="magenta",lwd=2)
HandMadeKernelDensity <- colSums(GaussianWeights)
lines(x_seq,HandMadeKernelDensity,col="orange",lwd=2)
# compare with shipped density
CannedDensity <- density(x,bw=Bandwidth,from=-4,to=4,n=gridpoints)
lines(CannedDensity,col="red",lwd=2,lty=2) # exactly the same wherever it is computed