核密度估计朱莉娅

时间:2015-09-04 09:18:37

标签: algorithm machine-learning julia

我正在尝试实现内核密度估算。但是我的代码没有提供它应该的答案。它也写在朱莉娅,但代码应该是自我解释。

以下是算法:

\$ f(x) = \frac{1}{n*h} * \sum_{i = 1}^n K(\frac{x - X_i}{h}) \$

,其中

\$ K(u) = 0.5*I(|u| <= 1)\$ with \$ u = \frac{x - X_i}{h}\$

因此,该算法测试x和观察X_i之间的距离是否小于1,这是由某个常数因子(binwidth)加权的。如果是这样,它会为该值指定0.5 /(n * h),其中n = #of observation。

这是我的实施:

#Kernel density function.
#Purpose: estimate the probability density function (pdf)
#of given observations
#@param data: observations for which the pdf should be estimated
#@return: returns an array with the estimated densities 

function kernelDensity(data)
|   
|   #Uniform kernel function. 
|   #@param x: Current x value
|   #@param X_i: x value of observation i
|   #@param width: binwidth
|   #@return: Returns 1 if the absolute distance from
|   #x(current) to x(observation) weighted by the binwidth
|   #is less then 1. Else it returns 0.
|  
|   function uniformKernel(x, observation, width)
|   |   u = ( x - observation ) / width
|   |   abs ( u ) <= 1 ? 1 : 0
|   end
|
|   #number of observations in the data set 
|   n = length(data)
|
|   #binwidth (set arbitraily to 0.1
|   h = 0.1 
|   
|   #vector that stored the pdf
|   res = zeros( Real, n )
|   
|   #counter variable for the loop 
|   counter = 0
|
|   #lower and upper limit of the x axis
|   start = floor(minimum(data))
|   stop = ceil (maximum(data))
|
|   #main loop
|   #@linspace: divides the space from start to stop in n
|   #equally spaced intervalls
|   for x in linspace(start, stop, n) 
|   |   counter += 1
|   |   for observation in data
|   |   |
|   |   |   #count all observations for which the kernel
|   |   |   #returns 1 and mult by 0.5 because the
|   |   |   #kernel computed the absolute difference which can be
|   |   |   #either positive or negative
|   |   |   res[counter] += 0.5 * uniformKernel(x, observation, h)
|   |   end
|   |   #devide by n times h
|   |   res[counter] /= n * h
|   end
|   #return results
|   res
end
#run function
#@rand: generates 10 uniform random numbers between 0 and 1
kernelDensity(rand(10))

并且正在返回:

> 0.0
> 1.5
> 2.5
> 1.0
> 1.5
> 1.0
> 0.0
> 0.5
> 0.5
> 0.0

其总和为:8.5(累积分配函数。应为1。)

所以有两个错误:

  1. 值未正确缩放。每个数字应约为其当前值的十分之一。事实上,如果观察次数增加10 ^ n n = 1,2,...那么cdf也会增加10 ^ n
  2. 例如:

    > kernelDensity(rand(1000))
    > 953.53 
    
    1. 它们不总和为10(如果不是缩放误差则为1)。随着样本量的增加,错误变得更加明显:大约有。 5%的观察结果未被包括在内。
    2. 我相信我实施了公式1:1,因此我真的不明白错误在哪里。

2 个答案:

答案 0 :(得分:5)

我不是KDE的专家,所以请尽一切努力,但是代码的实现非常相似(但要快得多!):

function kernelDensity{T<:AbstractFloat}(data::Vector{T}, h::T)
  res = similar(data)
  lb = minimum(data); ub = maximum(data)
  for (i,x) in enumerate(linspace(lb, ub, size(data,1)))
    for obs in data
      res[i] += abs((obs-x)/h) <= 1. ? 0.5 : 0.
    end
    res[i] /= (n*h)
 end
 sum(res)
end

如果我没弄错的话,密度估计值应该加到1,即我们希望kernelDensity(rand(100), 0.1)/100至少接近1.在上面的实现中,我到达那里,给予或采取5 %,但是我们再次不知道0.1是最佳带宽(使用h=0.135而不是我达到0.1%以内),并且已知均匀内核只有大约93%“有效”

在任何情况下,Julia都有一个非常好的内核密度包here,所以你可能应该只做Pkg.add("KernelDensity")而不是尝试编写你自己的Epanechnikov内核:)

答案 1 :(得分:3)

指出错误:你有n个大小为2h的B_i覆盖[0,1],随机点X落在预期的E \sum_{i = 1}^{n} 1{X \in B_i} = n E 1{X \in B_i} = 2 n h个数量的区间。你除以2 n h。

对于n个点,函数的期望值为enter image description here

实际上,你有一些大小不一的垃圾箱。小时。 (例如,如果start = 0,则第一个bin的一半在[0,1]之外),将此因子计算得出偏差。

编辑:顺便说一句,如果您认为这些箱子在[0,1]中有随机位置,则偏差很容易计算。然后,这些箱子平均缺少h / 2 =其大小的5%。