Repository: statsmodels/statsmodels

Issue:
Error for KDEUnivariate evaluation
#### Describe the bug

I’m trying to fit KDE model with different kernels when I encountered different behaviors.
#### Code Sample, a copy-pastable example if possible
```from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm

data = np.random.normal(size=10000)
x_grid = np.linspace(min(data), max(data), 10000)

# gaussian kernel

density = sm.nonparametric.KDEUnivariate(data) # scott or silverman or given, kernel=bi, cos, epa, epa2, gau, par, tri
density.fit(kernel="gau", fft=False)
density.evaluate(x_grid)

# triangular kernel

density = sm.nonparametric.KDEUnivariate(data) # scott or silverman or given, kernel=bi, cos, epa, epa2, gau, par, tri
density.fit(kernel="tri", fft=False)
density.evaluate(x_grid)

# cosine kernel
density = sm.nonparametric.KDEUnivariate(data)
density.fit(kernel="cos", fft=False)
density.evaluate(x_grid)
```
<details>

In the above examples, only gaussian model gives expected output while I got “ValueError" for triangular and cosine kernels:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-13-bffac78a98d0> in <module>
     10 density.fit(kernel="tri", fft=False)
     11 
---> 12 density.evaluate(x_grid)

~/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py in evaluate(self, point)
    253         """
    254         _checkisfit(self)
--> 255         return self.kernel.density(self.endog, point)
    256 
    257 

~/anaconda3/lib/python3.7/site-packages/statsmodels/sandbox/nonparametric/kernels.py in density(self, xs, x)
    192             xs, weights = self.in_domain( xs, self.weights, x )
    193         else:
--> 194             xs = self.in_domain( xs, xs, x )[0]
    195         xs = np.asarray(xs)
    196         #print 'len(xs)', len(xs), x

~/anaconda3/lib/python3.7/site-packages/statsmodels/sandbox/nonparametric/kernels.py in in_domain(self, xs, ys, x)
    176             return (xs, ys)
    177         else:
--> 178             filtered = lfilter(isInDomain, lzip(xs, ys))
    179             if len(filtered) > 0:
    180                 xs, ys = lzip(*filtered)

~/anaconda3/lib/python3.7/site-packages/statsmodels/compat/python.py in lfilter(*args, **kwargs)
     35 
     36 def lfilter(*args, **kwargs):
---> 37     return list(filter(*args, **kwargs))
     38 
     39 

~/anaconda3/lib/python3.7/site-packages/statsmodels/sandbox/nonparametric/kernels.py in isInDomain(xy)
    171             """Used for filter to check if point is in the domain"""
    172             u = (xy[0]-x)/self.h
--> 173             return u >= self.domain[0] and u <= self.domain[1]
    174 
    175         if self.domain is None:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

</details>


If the issue has not been resolved, please file it in the issue tracker.

#### Expected Output

A clear and concise description of what you expected to happen.

#### Output of ``import statsmodels.api as sm; sm.show_versions()``

<details>

INSTALLED VERSIONS
------------------
Python: 3.7.3.final.0
OS: Darwin 19.3.0 Darwin Kernel Version 19.3.0: Thu Jan  9 20:58:23 PST 2020; root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8

statsmodels
===========

Installed: 0.11.0 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/statsmodels)

Required Dependencies
=====================

cython: 0.29.15 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/Cython)
numpy: 1.18.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/numpy)
scipy: 1.4.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/scipy)
pandas: 1.0.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/pandas)
    dateutil: 2.8.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/dateutil)
patsy: 0.5.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/patsy)

Optional Dependencies
=====================

matplotlib: 3.1.3 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/matplotlib)
    backend: module://ipykernel.pylab.backend_inline 
cvxopt: Not installed
joblib: 0.14.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/joblib)

Developer Tools
================

IPython: 7.12.0 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/IPython)
    jinja2: 2.11.1 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/jinja2)
sphinx: 2.4.0 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/sphinx)
    pygments: 2.5.2 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/pygments)
pytest: 5.3.5 (/Users/jinzhenwang/anaconda3/lib/python3.7/site-packages/pytest)
virtualenv: Not installed

</details>



Discussion:

Resolving patch:
diff --git a/statsmodels/nonparametric/kde.py b/statsmodels/nonparametric/kde.py
--- a/statsmodels/nonparametric/kde.py
+++ b/statsmodels/nonparametric/kde.py
@@ -244,12 +244,12 @@ def icdf(self):
 
     def evaluate(self, point):
         """
-        Evaluate density at a single point.
+        Evaluate density at a point or points.
 
         Parameters
         ----------
-        point : float
-            Point at which to evaluate the density.
+        point : {float, ndarray}
+            Point(s) at which to evaluate the density.
         """
         _checkisfit(self)
         return self.kernel.density(self.endog, point)
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
@@ -170,7 +170,7 @@ def in_domain(self, xs, ys, x):
         def isInDomain(xy):
             """Used for filter to check if point is in the domain"""
             u = (xy[0]-x)/self.h
-            return u >= self.domain[0] and u <= self.domain[1]
+            return np.all((u >= self.domain[0]) & (u <= self.domain[1]))
 
         if self.domain is None:
             return (xs, ys)


Test patch:
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
@@ -1,9 +1,10 @@
 import numpy as np
 import numpy.testing as npt
-from numpy.testing import assert_allclose, assert_equal
 import pytest
+from numpy.testing import assert_allclose, assert_equal
 
 import statsmodels.api as sm
+
 nparam = sm.nonparametric
 
 
@@ -414,3 +415,13 @@ def test_efficient_user_specified_bw(self):
                                                           randomize=False,
                                                           n_sub=100))
         npt.assert_equal(dens.bw, bw_user)
+
+
+@pytest.mark.parametrize("kernel", ["biw", "cos", "epa", "gau",
+                                    "tri", "triw", "uni"])
+def test_all_kernels(kernel, reset_randomstate):
+    data = np.random.normal(size=200)
+    x_grid = np.linspace(min(data), max(data), 200)
+    density = sm.nonparametric.KDEUnivariate(data)
+    density.fit(kernel="gau", fft=False)
+    assert isinstance(density.evaluate(x_grid), np.ndarray)