Repository: statsmodels/statsmodels

Issue:
kernels cosine differs from Stata
Our definition of the sandbox.nonparametric.kernels.Cosine differes from Stata

Stata uses
`1 + cos(2 pi z)  for  |z| < 1/2 and 0 otherwise`

 I don't know what the normalization and _L2Norm is

we use

```
CustomKernel.__init__(self, shape=lambda x: 0.78539816339744828 *
                np.cos(np.pi/2.0 * x), h=h, domain=[-1.0, 1.0], norm = 1.0)
        self._L2Norm = np.pi**2/16.0
```

Wikipedia http://en.wikipedia.org/wiki/Kernel_(statistics)#Kernel_functions_in_common_use
has our Cosine kernel



Discussion:

Resolving patch:
diff --git a/statsmodels/examples/ex_kde_confint.py b/statsmodels/examples/ex_kde_confint.py
new file mode 100644
--- /dev/null
+++ b/statsmodels/examples/ex_kde_confint.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+"""
+
+Created on Mon Dec 16 11:02:59 2013
+
+Author: Josef Perktold
+"""
+
+import numpy as np
+from scipy import stats
+import matplotlib.pyplot as plt
+import statsmodels.nonparametric.api as npar
+from statsmodels.sandbox.nonparametric import kernels
+from statsmodels.distributions.mixture_rvs import mixture_rvs
+
+# example from test_kde.py mixture of two normal distributions
+np.random.seed(12345)
+x = mixture_rvs([.25,.75], size=200, dist=[stats.norm, stats.norm],
+                kwargs = (dict(loc=-1, scale=.5),dict(loc=1, scale=.5)))
+
+x.sort() # not needed
+
+kde = npar.KDEUnivariate(x)
+kde.fit('gau')
+ci = kde.kernel.density_confint(kde.density, len(x))
+
+fig = plt.figure()
+ax = fig.add_subplot(1, 1, 1)
+ax.hist(x, bins=15, normed=True, alpha=0.25)
+ax.plot(kde.support, kde.density, lw=2, color='red')
+ax.fill_between(kde.support, ci[:,0], ci[:,1],
+                    color='grey', alpha='0.7')
+ax.set_title('Kernel Density Gaussian (bw = %4.2f)' % kde.bw)
+
+
+# use all kernels directly
+
+x_grid = np.linspace(np.min(x), np.max(x), 51)
+x_grid = np.linspace(-3, 3, 51)
+
+kernel_names = ['Biweight', 'Cosine', 'Epanechnikov', 'Gaussian',
+                'Triangular', 'Triweight', #'Uniform',
+                ]
+
+fig = plt.figure()
+for ii, kn in enumerate(kernel_names):
+    ax = fig.add_subplot(2, 3, ii+1)   # without uniform
+    ax.hist(x, bins=10, normed=True, alpha=0.25)
+    #reduce bandwidth for Gaussian and Uniform which are to large in example
+    if kn in ['Gaussian', 'Uniform']:
+        args = (0.5,)
+    else:
+        args = ()
+    kernel = getattr(kernels, kn)(*args)
+
+    kde_grid = [kernel.density(x, xi) for xi in x_grid]
+    confint_grid = kernel.density_confint(kde_grid, len(x))
+
+    ax.plot(x_grid, kde_grid, lw=2, color='red', label=kn)
+    ax.fill_between(x_grid, confint_grid[:,0], confint_grid[:,1],
+                    color='grey', alpha='0.7')
+    ax.legend(loc='upper left')
+
+plt.show()
diff --git a/statsmodels/nonparametric/kde.py b/statsmodels/nonparametric/kde.py
--- a/statsmodels/nonparametric/kde.py
+++ b/statsmodels/nonparametric/kde.py
@@ -29,7 +29,7 @@
 kernel_switch = dict(gau=kernels.Gaussian, epa=kernels.Epanechnikov,
                     uni=kernels.Uniform, tri=kernels.Triangular,
                     biw=kernels.Biweight, triw=kernels.Triweight,
-                    cos=kernels.Cosine)
+                    cos=kernels.Cosine, cos2=kernels.Cosine2)
 
 def _checkisfit(self):
     try:
@@ -150,6 +150,9 @@ def fit(self, kernel="gau", bw="scott", fft=True, weights=None,
         self.kernel = kernel_switch[kernel](h=bw) # we instantiate twice,
                                                 # should this passed to funcs?
         # put here to ensure empty cache after re-fit with new options
+        self.kernel.weights = weights
+        if weights is not None:
+            self.kernel.weights /= weights.sum()
         self._cache = resettable_cache()
 
     @cache_readonly
@@ -334,6 +337,8 @@ def kdensity(X, kernel="gau", bw="scott", weights=None, gridsize=None,
         weights = np.ones(nobs)
         q = nobs
     else:
+        # ensure weights is a numpy array
+        weights = np.asarray(weights)
         if len(weights) != len(clip_x):
             msg = "The length of the weights must be the same as the given X."
             raise ValueError(msg)
diff --git a/statsmodels/sandbox/nonparametric/kernels.py b/statsmodels/sandbox/nonparametric/kernels.py
--- a/statsmodels/sandbox/nonparametric/kernels.py
+++ b/statsmodels/sandbox/nonparametric/kernels.py
@@ -48,6 +48,7 @@ def __init__(self, n, kernels = None, H = None):
             kernels = Gaussian()
 
         self._kernels = kernels
+        self.weights = None
 
         if H is None:
             H = np.matrix( np.identity(n))
@@ -66,13 +67,17 @@ def setH(self, value):
     H = property(getH, setH, doc="Kernel bandwidth matrix")
 
     def density(self, xs, x):
+
         n = len(xs)
-        #xs = self.inDomain( xs, xs, x )[0]
+        #xs = self.in_domain( xs, xs, x )[0]
 
         if len(xs)>0:  ## Need to do product of marginal distributions
             #w = np.sum([self(self._Hrootinv * (xx-x).T ) for xx in xs])/n
             #vectorized doesn't work:
-            w = np.mean(self((xs-x) * self._Hrootinv )) #transposed
+            if self.weights is not None:
+                w = np.mean(self((xs-x) * self._Hrootinv).T * self.weights)/sum(self.weights)
+            else:
+                w = np.mean(self((xs-x) * self._Hrootinv )) #transposed
             #w = np.mean([self(xd) for xd in ((xs-x) * self._Hrootinv)] ) #transposed
             return w
         else:
@@ -112,37 +117,37 @@ class CustomKernel(object):
 
     def __init__(self, shape, h = 1.0, domain = None, norm = None):
         """
-        shape should be a lambda taking and returning numeric type.
+        shape should be a function taking and returning numeric type.
 
         For sanity it should always return positive or zero but this isn't
-        enforced incase you want to do weird things.  Bear in mind that the
+        enforced in case you want to do weird things. Bear in mind that the
         statistical tests etc. may not be valid for non-positive kernels.
 
         The bandwidth of the kernel is supplied as h.
 
-        You may specify a domain as a list of 2 values [min,max], in which case
-        kernel will be treated as zero outside these values.  This will speed up
+        You may specify a domain as a list of 2 values [min, max], in which case
+        kernel will be treated as zero outside these values. This will speed up
         calculation.
 
         You may also specify the normalisation constant for the supplied Kernel.
         If you do this number will be stored and used as the normalisation
         without calculation.  It is recommended you do this if you know the
         constant, to speed up calculation.  In particular if the shape function
-        provided is already normalised you should provide
-        norm = 1.0
-        or
-        norm = True
+        provided is already normalised you should provide norm = 1.0.
+
+        Warning: I think several calculations assume that the kernel is
+        normalized. No tests for non-normalized kernel.
         """
-        if norm is True:
-            norm = 1.0
-        self._normconst = norm
+        self._normconst = norm   # a value or None, if None, then calculate
         self.domain = domain
+        self.weights = None
         if callable(shape):
             self._shape = shape
         else:
             raise TypeError("shape must be a callable object/function")
         self._h = h
         self._L2Norm = None
+        self._kernel_var = None
 
     def geth(self):
         """Getter for kernel bandwidth, h"""
@@ -152,7 +157,7 @@ def seth(self, value):
         self._h = value
     h = property(geth, seth, doc="Kernel Bandwidth")
 
-    def inDomain(self, xs, ys, x):
+    def in_domain(self, xs, ys, x):
         """
         Returns the filtered (xs, ys) based on the Kernel domain centred on x
         """
@@ -179,23 +184,90 @@ def density(self, xs, x):
         xs
         """
         xs = np.asarray(xs)
-        n = len(xs) # before inDomain?
-        xs = self.inDomain( xs, xs, x )[0]
+        n = len(xs) # before in_domain?
+        if self.weights is not None:
+            xs, weights = self.in_domain( xs, self.weights, x )
+        else:
+            xs = self.in_domain( xs, xs, x )[0]
+        xs = np.asarray(xs)
+        #print 'len(xs)', len(xs), x
         if xs.ndim == 1:
             xs = xs[:,None]
         if len(xs)>0:
             h = self.h
-            w = 1/h * np.mean(self((xs-x)/h), axis=0)
+            if self.weights is not None:
+                w = 1 / h * np.sum(self((xs-x)/h).T * weights, axis=1)
+            else:
+                w = 1. / (h * n) * np.sum(self((xs-x)/h), axis=0)
             return w
         else:
             return np.nan
 
+    def density_var(self, density, nobs):
+        """approximate pointwise variance for kernel density
+
+        not verified
+
+        Parameters
+        ----------
+        density : array_lie
+            pdf of the kernel density
+        nobs : int
+            number of observations used in the KDE estimation
+
+        Returns
+        -------
+        kde_var : ndarray
+            estimated variance of the density estimate
+
+        Notes
+        -----
+        This uses the asymptotic normal approximation to the distribution of
+        the density estimate.
+        """
+        return np.asarray(density) * self.L2Norm / self.h / nobs
+
+    def density_confint(self, density, nobs, alpha=0.05):
+        """approximate pointwise confidence interval for kernel density
+
+        The confidence interval is centered at the estimated density and
+        ignores the bias of the density estimate.
+
+        not verified
+
+        Parameters
+        ----------
+        density : array_lie
+            pdf of the kernel density
+        nobs : int
+            number of observations used in the KDE estimation
+
+        Returns
+        -------
+        conf_int : ndarray
+            estimated confidence interval of the density estimate, lower bound
+            in first column and upper bound in second column
+
+        Notes
+        -----
+        This uses the asymptotic normal approximation to the distribution of
+        the density estimate. The lower bound can be negative for density
+        values close to zero.
+
+        """
+        from scipy import stats
+        crit = stats.norm.isf(alpha / 2.)
+        density = np.asarray(density)
+        half_width = crit * np.sqrt(self.density_var(density, nobs))
+        conf_int = np.column_stack((density - half_width, density + half_width))
+        return conf_int
+
     def smooth(self, xs, ys, x):
         """Returns the kernel smoothing estimate for point x based on x-values
         xs and y-values ys.
         Not expected to be called by the user.
         """
-        xs, ys = self.inDomain(xs, ys, x)
+        xs, ys = self.in_domain(xs, ys, x)
 
         if len(xs)>0:
             w = np.sum(self((xs-x)/self.h))
@@ -208,7 +280,7 @@ def smooth(self, xs, ys, x):
     def smoothvar(self, xs, ys, x):
         """Returns the kernel smoothing estimate of the variance at point x.
         """
-        xs, ys = self.inDomain(xs, ys, x)
+        xs, ys = self.in_domain(xs, ys, x)
 
         if len(xs) > 0:
             fittedvals = np.array([self.smooth(xs, ys, xx) for xx in xs])
@@ -222,7 +294,7 @@ def smoothvar(self, xs, ys, x):
     def smoothconf(self, xs, ys, x):
         """Returns the kernel smoothing estimate with confidence 1sigma bounds
         """
-        xs, ys = self.inDomain(xs, ys, x)
+        xs, ys = self.in_domain(xs, ys, x)
 
         if len(xs) > 0:
             fittedvals = np.array([self.smooth(xs, ys, xx) for xx in xs])
@@ -266,6 +338,18 @@ def norm_const(self):
             self._normconst = 1.0/(quadres[0])
         return self._normconst
 
+    @property
+    def kernel_var(self):
+        """Returns the second moment of the kernel"""
+        if self._kernel_var is None:
+            func = lambda x: x**2 * self.norm_const * self._shape(x)
+            if self.domain is None:
+                self._kernel_var = scipy.integrate.quad(func, -inf, inf)[0]
+            else:
+                self._kernel_var = scipy.integrate.quad(func, self.domain[0],
+                                               self.domain[1])[0]
+        return self._kernel_var
+
     def weight(self, x):
         """This returns the normalised weight at distance x"""
         return self.norm_const*self._shape(x)
@@ -278,29 +362,37 @@ def __call__(self, x):
         """
         return self._shape(x)
 
+
 class Uniform(CustomKernel):
     def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape=lambda x: 0.5, h=h,
                               domain=[-1.0, 1.0], norm = 1.0)
         self._L2Norm = 0.5
+        self._kernel_var = 1. / 3
+
 
 class Triangular(CustomKernel):
     def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape=lambda x: 1 - abs(x), h=h,
                               domain=[-1.0, 1.0], norm = 1.0)
         self._L2Norm = 2.0/3.0
+        self._kernel_var = 1. / 6
+
 
 class Epanechnikov(CustomKernel):
     def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape=lambda x: 0.75*(1 - x*x), h=h,
                               domain=[-1.0, 1.0], norm = 1.0)
         self._L2Norm = 0.6
+        self._kernel_var = 0.2
+
 
 class Biweight(CustomKernel):
     def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape=lambda x: 0.9375*(1 - x*x)**2, h=h,
                               domain=[-1.0, 1.0], norm = 1.0)
         self._L2Norm = 5.0/7.0
+        self._kernel_var = 1. / 7
 
     def smooth(self, xs, ys, x):
         """Returns the kernel smoothing estimate for point x based on x-values
@@ -309,7 +401,7 @@ def smooth(self, xs, ys, x):
 
         Special implementation optimised for Biweight.
         """
-        xs, ys = self.inDomain(xs, ys, x)
+        xs, ys = self.in_domain(xs, ys, x)
 
         if len(xs) > 0:
             w = np.sum(square(subtract(1, square(divide(subtract(xs, x),
@@ -324,7 +416,7 @@ def smoothvar(self, xs, ys, x):
         """
         Returns the kernel smoothing estimate of the variance at point x.
         """
-        xs, ys = self.inDomain(xs, ys, x)
+        xs, ys = self.in_domain(xs, ys, x)
 
         if len(xs) > 0:
             fittedvals = np.array([self.smooth(xs, ys, xx) for xx in xs])
@@ -340,7 +432,7 @@ def smoothvar(self, xs, ys, x):
     def smoothconf(self, xs, ys, x):
         """Returns the kernel smoothing estimate with confidence 1sigma bounds
         """
-        xs, ys = self.inDomain(xs, ys, x)
+        xs, ys = self.in_domain(xs, ys, x)
 
         if len(xs) > 0:
             fittedvals = np.array([self.smooth(xs, ys, xx) for xx in xs])
@@ -363,6 +455,8 @@ def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape=lambda x: 1.09375*(1 - x*x)**3, h=h,
                               domain=[-1.0, 1.0], norm = 1.0)
         self._L2Norm = 350.0/429.0
+        self._kernel_var = 1. / 9
+
 
 class Gaussian(CustomKernel):
     """
@@ -374,6 +468,7 @@ def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape = lambda x: 0.3989422804014327 *
                         np.exp(-x**2/2.0), h = h, domain = None, norm = 1.0)
         self._L2Norm = 1.0/(2.0*np.sqrt(np.pi))
+        self._kernel_var = 1.0
 
     def smooth(self, xs, ys, x):
         """Returns the kernel smoothing estimate for point x based on x-values
@@ -398,3 +493,19 @@ def __init__(self, h=1.0):
         CustomKernel.__init__(self, shape=lambda x: 0.78539816339744828 *
                 np.cos(np.pi/2.0 * x), h=h, domain=[-1.0, 1.0], norm = 1.0)
         self._L2Norm = np.pi**2/16.0
+        self._kernel_var = 0.1894305308612978 # = 1 - 8 / np.pi**2
+
+
+class Cosine2(CustomKernel):
+    """
+    Cosine2 Kernel
+
+    K(u) = 1 + cos(2 * pi * u) between -0.5 and 0.5
+
+    Note: this  is the same Cosine kernel that Stata uses
+    """
+    def __init__(self, h=1.0):
+        CustomKernel.__init__(self, shape=lambda x: 1 + np.cos(2.0 * np.pi * x)
+                , h=h, domain=[-0.5, 0.5], norm = 1.0)
+        self._L2Norm = 1.5
+        self._kernel_var = 0.03267274151216444  # = 1/12. - 0.5 / np.pi**2


Test patch:
diff --git a/statsmodels/nonparametric/tests/results/results_kde_univ_weights.csv b/statsmodels/nonparametric/tests/results/results_kde_univ_weights.csv
new file mode 100644
--- /dev/null
+++ b/statsmodels/nonparametric/tests/results/results_kde_univ_weights.csv
@@ -0,0 +1,61 @@
+x,weights,x_gau_wd,x_epa_wd,x_epan2_wd,x_bi_wd,x_par_wd,x_tri_wd,x_cos_wd,x_rec_wd
+.50370319,.60467487,.27195577,.27358917,.28859682,.30192719,.31283399,.29793749,.31904846,.25210428
+.41308592,.60446553,.28380106,.28108376,.3181792,.32885763,.34548845,.32667957,.3553171,.3010559
+-1.13905,.84897398,.26095184,.24941442,.32307763,.34872245,.38725314,.34866867,.42649716,.30003257
+.66034203,.89616498,.2498343,.25886883,.24541009,.23760925,.23485521,.24529473,.23002624,.2627293
+.46479403,.58916777,.27716742,.27709199,.30071519,.31461812,.3288969,.31115181,.33274659,.27792568
+-.30933686,.92004553,.314696,.31804133,.34663542,.35808323,.38279667,.36339898,.44094594,.31994675
+-.64959307,.73271562,.29736955,.31088207,.25708713,.24806417,.2539103,.26299651,.28856418,.30887871
+.68375801,.31006212,.24646059,.25568508,.23632094,.22824775,.22264335,.23446073,.21686179,.2512418
+-.64383424,.93079,.2977045,.3114788,.25702669,.2509086,.25709325,.26391628,.29057321,.30887871
+.42128692,.31271088,.28277417,.28032663,.315695,.3267261,.34319264,.32450914,.35120708,.3010559
+1.0328139,.6187646,.19865928,.20562102,.1561914,.14491215,.14684135,.15591422,.16079396,.19385198
+-1.2904935,.95351446,.24177169,.22953008,.32501344,.38069438,.4611249,.36532316,.54515641,.23436556
+.78787175,.32106611,.23156764,.24078401,.20218871,.19425712,.17622449,.19531208,.16070512,.21671793
+1.5157072,.66341627,.13665126,.12703345,.17581124,.20039954,.25847706,.20674992,.35899704,.15119583
+-.27648715,.39563784,.3158193,.31586931,.3481959,.35810566,.38200715,.36407338,.42546061,.31994675
+-.22376217,.52433377,.31726241,.31194313,.35102832,.35601176,.37307496,.35644025,.38768768,.37555419
+1.3974312,.51672953,.15280551,.1444295,.19194916,.21056571,.22903207,.20154176,.23748733,.15119583
+1.5038735,.89413116,.13831766,.12874814,.17849067,.20256748,.25945018,.20968897,.3582845,.15119583
+-.47890497,.97143796,.30692589,.32055942,.30950537,.32610177,.33693342,.31654009,.30584078,.27657124
+-.1359496,.14135297,.31850248,.30708612,.35335067,.356503,.35032857,.34820511,.32358697,.36376222
+-.73032662,.52783029,.29259949,.30296315,.24788827,.21783998,.2070478,.23766242,.22559505,.30464283
+-.03327709,.86167095,.31776682,.30409739,.35990479,.35322891,.33260293,.35696834,.31130253,.35590704
+.28115083,.95467052,.2986962,.29242144,.33561626,.34984221,.36523791,.34821892,.38413714,.30800325
+-1.2989147,.80130654,.24054457,.228406,.3242507,.37937907,.45917069,.36459455,.5425096,.23436556
+-2.8194873,.29377041,.03423861,.03397935,.01992567,.01845811,.02368624,.02238546,.03521327,.02886914
+-.85198542,.07745348,.28499181,.28840952,.24395463,.21358347,.16033312,.21929099,.08660478,.27849863
+-1.1069518,.49668391,.26433879,.25400233,.32229855,.33312293,.35156483,.33496984,.37965384,.30003257
+-.93773148,.19808942,.27902143,.27775656,.27754821,.24108994,.1807995,.24935348,.0900434,.32253643
+-1.5377704,.325444,.19963066,.19249631,.23509243,.22351351,.20130053,.22380662,.15481808,.23739545
+.5557587,.24841208,.2647569,.26995034,.27512558,.28157636,.28811611,.27768035,.30157454,.27132668
+-2.277282,.75179027,.07828852,.07873796,.07875061,.09204377,.11446709,.0922851,.15025998,.05700553
+-.39020054,.13177808,.31133267,.32104843,.33517543,.35027672,.36907311,.34495022,.40165181,.29228903
+1.2071225,.36595759,.17694573,.17676473,.18418833,.17480444,.15023094,.17594001,.12570809,.17381817
+.17869009,.61706068,.30769076,.29939275,.34442456,.347134,.35602386,.34847054,.36011693,.36522916
+-1.0041675,.1042514,.27381383,.26920255,.29856445,.27641696,.23392586,.27843723,.19447102,.29342568
+-1.3776266,.92715643,.22828438,.21635169,.30823856,.35053859,.40838262,.34325694,.47623359,.23204453
+.49928077,.16346328,.2725563,.27403682,.28985754,.30351024,.31480322,.2993623,.32039228,.25210428
+-1.4052563,.96126524,.22366107,.21181391,.29911819,.33392962,.37877909,.32774722,.43913813,.23204453
+.16256489,.38334264,.30887154,.30012656,.34572104,.34707247,.35358542,.34768253,.3560065,.35593763
+-.06778502,.77186504,.31829891,.30559493,.35826195,.3554906,.33637638,.35551408,.305725,.34139722
+-1.2600064,.12778838,.24607265,.2333853,.32622041,.38243622,.46216935,.3612336,.54549954,.23436556
+-1.1328956,.49085611,.26161854,.25023928,.3231961,.34583442,.38073288,.34660716,.41805519,.30003257
+-2.0064812,.48090321,.11322783,.12879922,.0646241,.06034824,.05482294,.06467009,.05764425,.066758
+.30101641,.13432719,.29667228,.29063393,.33480316,.34885982,.36454689,.34424398,.38577022,.29229072
+.05911705,.72259881,.31483242,.30162583,.360174,.34909635,.33698463,.35400964,.32926111,.3572589
+1.1384692,.43570061,.18542783,.18785159,.17569715,.15617806,.14519084,.16879885,.15817458,.20996476
+-2.4006338,.0702726,.0661328,.05973222,.07546614,.08153031,.09764932,.08181684,.12409822,.06580884
+-.28085336,.91503527,.31567929,.31617356,.34821096,.35820009,.38234837,.36478543,.42807283,.31994675
+.02565318,.21581449,.31615397,.3015383,.36059293,.34980376,.33308907,.35373627,.32250968,.37892557
+-1.3860712,.52139452,.22688798,.21494935,.30566124,.34579032,.39987794,.33922414,.46560575,.23204453
+.86393679,.4338506,.22096543,.22892366,.1834737,.1740142,.15644199,.17635877,.12724629,.18408431
+.25246163,.90097287,.30146488,.29459784,.33628211,.35007398,.36442492,.34886654,.38021707,.29838197
+1.5005706,.52615659,.13878075,.12921959,.17919626,.20313531,.25952209,.20982585,.35751304,.15119583
+1.0532023,.23850945,.19607429,.20225294,.1596919,.1452661,.14646528,.15783051,.16537447,.19385198
+-2.3385949,.03208453,.07201598,.06861114,.07788137,.08815269,.11183084,.08955035,.15620679,.06580884
+-.37427851,.80847815,.31205326,.32060023,.33786515,.35297134,.37326996,.3501085,.41938363,.29228903
+-2.3599583,.56724767,.06994132,.06564237,.07736941,.08614625,.10817961,.08872598,.14898803,.06580884
+-1.1578862,.98669892,.25885895,.24676433,.32192541,.35726098,.40592428,.35127741,.45134312,.30003257
+-.55186497,.1049692,.30295427,.31836946,.28119002,.29690047,.30298875,.28439032,.29007716,.24740666
+1.5926728,.78610254,.12554701,.11550205,.15819727,.18163449,.22681772,.17941142,.29077747,.12550619
diff --git a/statsmodels/nonparametric/tests/test_kde.py b/statsmodels/nonparametric/tests/test_kde.py
--- a/statsmodels/nonparametric/tests/test_kde.py
+++ b/statsmodels/nonparametric/tests/test_kde.py
@@ -1,5 +1,6 @@
 import os
 import numpy.testing as npt
+from nose import SkipTest
 import numpy as np
 from statsmodels.distributions.mixture_rvs import mixture_rvs
 from statsmodels.nonparametric.kde import KDEUnivariate as KDE
@@ -12,6 +13,9 @@
 #print rfname
 KDEResults = np.genfromtxt(open(rfname, 'rb'), delimiter=",", names=True)
 
+rfname = os.path.join(curdir,'results','results_kde_univ_weights.csv')
+KDEWResults = np.genfromtxt(open(rfname, 'rb'), delimiter=",", names=True)
+
 # setup test data
 
 np.random.seed(12345)
@@ -19,11 +23,28 @@
                 kwargs = (dict(loc=-1,scale=.5),dict(loc=1,scale=.5)))
 
 class CheckKDE(object):
+
     decimal_density = 7
+
     def test_density(self):
         npt.assert_almost_equal(self.res1.density, self.res_density,
                 self.decimal_density)
 
+    def test_evaluate(self):
+        # disable test
+        # fails for Epan, Triangular and Biweight, only Gaussian is correct
+        # added it as test method to TestKDEGauss below
+        # inDomain is not vectorized
+        #kde_vals = self.res1.evaluate(self.res1.support)
+        kde_vals = [self.res1.evaluate(xi) for xi in self.res1.support]
+        kde_vals = np.squeeze(kde_vals)  #kde_vals is a "column_list"
+        mask_valid = np.isfinite(kde_vals)
+        # TODO: nans at the boundaries
+        kde_vals[~mask_valid] = 0
+        npt.assert_almost_equal(kde_vals, self.res_density,
+                                self.decimal_density)
+
+
 class TestKDEGauss(CheckKDE):
     @classmethod
     def setupClass(cls):
@@ -32,6 +53,17 @@ def setupClass(cls):
         cls.res1 = res1
         cls.res_density = KDEResults["gau_d"]
 
+    def test_evaluate(self):
+        #kde_vals = self.res1.evaluate(self.res1.support)
+        kde_vals = [self.res1.evaluate(xi) for xi in self.res1.support]
+        kde_vals = np.squeeze(kde_vals)  #kde_vals is a "column_list"
+        mask_valid = np.isfinite(kde_vals)
+        # TODO: nans at the boundaries
+        kde_vals[~mask_valid] = 0
+        npt.assert_almost_equal(kde_vals, self.res_density,
+                                self.decimal_density)
+
+
 class TestKDEEpanechnikov(CheckKDE):
     @classmethod
     def setupClass(cls):
@@ -67,6 +99,7 @@ def setupClass(cls):
 
 #weighted estimates taken from matlab so we can allow len(weights) != gridsize
 class TestKdeWeights(CheckKDE):
+
     @classmethod
     def setupClass(cls):
         res1 = KDE(Xi)
@@ -77,6 +110,16 @@ def setupClass(cls):
         rfname = os.path.join(curdir,'results','results_kde_weights.csv')
         cls.res_density = np.genfromtxt(open(rfname, 'rb'), skip_header=1)
 
+    def test_evaluate(self):
+        #kde_vals = self.res1.evaluate(self.res1.support)
+        kde_vals = [self.res1.evaluate(xi) for xi in self.res1.support]
+        kde_vals = np.squeeze(kde_vals)  #kde_vals is a "column_list"
+        mask_valid = np.isfinite(kde_vals)
+        # TODO: nans at the boundaries
+        kde_vals[~mask_valid] = 0
+        npt.assert_almost_equal(kde_vals, self.res_density,
+                                self.decimal_density)
+
 
 class TestKDEGaussFFT(CheckKDE):
     @classmethod
@@ -88,6 +131,125 @@ def setupClass(cls):
         rfname2 = os.path.join(curdir,'results','results_kde_fft.csv')
         cls.res_density = np.genfromtxt(open(rfname2, 'rb'))
 
+class CheckKDEWeights(object):
+
+    @classmethod
+    def setupClass(cls):
+        cls.x = x = KDEWResults['x']
+        weights = KDEWResults['weights']
+        res1 = KDE(x)
+        res1.fit(kernel=cls.kernel_name, weights=weights, fft=False)
+        cls.res1 = res1
+        cls.res_density = KDEWResults[cls.res_kernel_name]
+
+    decimal_density = 7
+
+    def t_est_density(self):
+        npt.assert_almost_equal(self.res1.density, self.res_density,
+                self.decimal_density)
+
+    def test_evaluate(self):
+        if self.kernel_name == 'cos':
+            raise SkipTest("Cosine kernel fails against Stata")
+        kde_vals = [self.res1.evaluate(xi) for xi in self.x]
+        kde_vals = np.squeeze(kde_vals)  #kde_vals is a "column_list"
+        npt.assert_almost_equal(kde_vals, self.res_density,
+                                self.decimal_density)
+
+    def test_compare(self):
+        xx = self.res1.support
+        kde_vals = [self.res1.evaluate(xi) for xi in xx]
+        kde_vals = np.squeeze(kde_vals)  #kde_vals is a "column_list"
+        mask_valid = np.isfinite(kde_vals)
+        # TODO: nans at the boundaries
+        kde_vals[~mask_valid] = 0
+        npt.assert_almost_equal(self.res1.density, kde_vals,
+                                self.decimal_density)
+
+        # regression test, not compared to another package
+        nobs = len(self.res1.endog)
+        kern = self.res1.kernel
+        v = kern.density_var(kde_vals, nobs)
+        v_direct = kde_vals * kern.L2Norm / kern.h / nobs
+        npt.assert_allclose(v, v_direct, rtol=1e-10)
+
+        ci = kern.density_confint(kde_vals, nobs)
+        crit = 1.9599639845400545 #stats.norm.isf(0.05 / 2)
+        hw = kde_vals - ci[:, 0]
+        npt.assert_allclose(hw, crit * np.sqrt(v), rtol=1e-10)
+        hw = ci[:, 1] - kde_vals
+        npt.assert_allclose(hw, crit * np.sqrt(v), rtol=1e-10)
+
+    def test_kernel_constants(self):
+        kern = self.res1.kernel
+
+        nc = kern.norm_const
+        # trigger numerical integration
+        kern._norm_const = None
+        nc2 = kern.norm_const
+        npt.assert_allclose(nc, nc2, rtol=1e-10)
+
+        l2n = kern.L2Norm
+        # trigger numerical integration
+        kern._L2Norm = None
+        l2n2 = kern.L2Norm
+        npt.assert_allclose(l2n, l2n2, rtol=1e-10)
+
+        v = kern.kernel_var
+        # trigger numerical integration
+        kern._kernel_var = None
+        v2 = kern.kernel_var
+        npt.assert_allclose(v, v2, rtol=1e-10)
+
+
+class TestKDEWGauss(CheckKDEWeights):
+
+    kernel_name = "gau"
+    res_kernel_name = "x_gau_wd"
+
+
+class TestKDEWEpa(CheckKDEWeights):
+
+    kernel_name = "epa"
+    res_kernel_name = "x_epan2_wd"
+
+
+class TestKDEWTri(CheckKDEWeights):
+
+    kernel_name = "tri"
+    res_kernel_name = "x_" + kernel_name + "_wd"
+
+
+class TestKDEWBiw(CheckKDEWeights):
+
+    kernel_name = "biw"
+    res_kernel_name = "x_bi_wd"
+
+
+class TestKDEWCos(CheckKDEWeights):
+
+    kernel_name = "cos"
+    res_kernel_name = "x_cos_wd"
+
+
+class TestKDEWCos2(CheckKDEWeights):
+
+    kernel_name = "cos2"
+    res_kernel_name = "x_cos_wd"
+
+
+class T_estKDEWRect(CheckKDEWeights):
+    #TODO in docstring but not in kernel_switch
+    kernel_name = "rect"
+    res_kernel_name = "x_rec_wd"
+
+
+class T_estKDEWPar(CheckKDEWeights):
+    # TODO in docstring but not implemented in kernels
+    kernel_name = "par"
+    res_kernel_name = "x_par_wd"
+
+
 class test_kde_refit():
     np.random.seed(12345)
     data1 = np.random.randn(100) * 100
diff --git a/statsmodels/nonparametric/tests/test_kernel_density.py b/statsmodels/nonparametric/tests/test_kernel_density.py
--- a/statsmodels/nonparametric/tests/test_kernel_density.py
+++ b/statsmodels/nonparametric/tests/test_kernel_density.py
@@ -58,6 +58,62 @@ def setUp(self):
        0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
        0, 0, 0, 0]
 
+        self.weights = np.random.random(nobs)
+
+
+class TestKDEUnivariate(MyTest):
+
+    def test_pdf_non_fft(self):
+
+        kde = nparam.KDEUnivariate(self.noise)
+        kde.fit(fft=False)
+
+
+        grid = kde.support
+        testx = [grid[10*i] for i in range(6)]
+
+        # Test against values from R 'ks' package
+        kde_expected = [0.00016808277984236013,
+                        0.030759614592368954,
+                        0.14123404934759243,
+                        0.28807147408162409,
+                        0.25594519303876273,
+                        0.056593973915651047]
+
+        kde_vals0 = kde.density[10 * np.arange(6)]
+        kde_vals = kde.evaluate(testx)
+
+        npt.assert_allclose(kde_vals, kde_expected,
+                            atol=1e-6)
+        npt.assert_allclose(kde_vals0, kde_expected,
+                            atol=1e-6)
+
+
+    def test_weighted_pdf_non_fft(self):
+
+        kde = nparam.KDEUnivariate(self.noise)
+        kde.fit(weights=self.weights, fft=False)
+
+        grid = kde.support
+        testx = [grid[10*i] for i in range(6)]
+
+        # Test against values from R 'ks' package
+        kde_expected = [9.1998858033950757e-05,
+                        0.018761981151370496,
+                        0.14425925509365087,
+                        0.30307631742267443,
+                        0.2405445849994125,
+                        0.06433170684797665]
+
+        kde_vals0 = kde.density[10 * np.arange(6)]
+        kde_vals = kde.evaluate(testx)
+
+        npt.assert_allclose(kde_vals, kde_expected,
+                            atol=1e-6)
+        npt.assert_allclose(kde_vals0, kde_expected,
+                            atol=1e-6)
+
+
 
 class TestKDEMultivariate(MyTest):
     @dec.slow
@@ -305,3 +361,7 @@ def test_continuous_cvml_efficient(self):
         bw_expected = np.array([0.73387, 0.43715])
         npt.assert_allclose(dens_efficient.bw, bw_expected, atol=0, rtol=1e-3)
 
+if __name__ == "__main__":
+    import nose
+    nose.runmodule(argv=[__file__,'-vvs','-x','--pdb'],
+                       exit=False)
\ No newline at end of file