Repository: statsmodels/statsmodels

Issue:
BUG: statsmodels.regression.linear_model.OLS.fit_regularized fails to generate correct answer
#### Describe the bug

`statsmodels.regression.linear_model.OLS.fit_regularized` does not work when `alpha` is a list and `L1_wt=0`.

`statsmodels.regression.linear_model.OLS.fit_regularized` with `alpha` being a list of `0` does not match `statsmodels.regression.linear_model.OLS.fit`.

#### Code Sample, a copy-pastable example if possible

```python
import numpy as np
import statsmodels.regression.linear_model as linear_model
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
y = np.array([1, 2, 3])
model = linear_model.OLS(y, X)
results = model.fit_regularized(alpha=[0, 0, 0], L1_wt=0.)
print(results.params)
```

The error message:

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-81-767ef847f3da> in <module>
      4 y = np.array([1, 2, 3])
      5 model = linear_model.OLS(y, X)
----> 6 results = model.fit_regularized(alpha=[0, 0, 0], L1_wt=0.)

D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\statsmodels\regression\linear_model.py in fit_regularized(self, method, alpha, L1_wt, start_params, profile_scale, refit, **kwargs)
   1044 
   1045         if L1_wt == 0:
-> 1046             return self._fit_ridge(alpha)
   1047 
   1048         # If a scale parameter is passed in, the non-profile

D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\statsmodels\regression\linear_model.py in _fit_ridge(self, alpha)
   1141             params = np.dot(v, params)
   1142         else:
-> 1143             vtav = self.nobs * np.dot(vt, alpha[:, None] * v)
   1144             d = np.diag(vtav) + s2
   1145             np.fill_diagonal(vtav, d)

TypeError: list indices must be integers or slices, not tuple
```



#### Expected Output

It should print `[-0.05555556  0.11111111  0.27777778]`, which is the output of the following code:

```python
import numpy as np
import statsmodels.regression.linear_model as linear_model
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
y = np.array([1, 2, 3])
model = linear_model.OLS(y, X)
results = model.fit()
print(results.params)
```

#### Output of ``import statsmodels.api as sm; sm.show_versions()``

<details>

INSTALLED VERSIONS
------------------
Python: 3.8.1.final.0

statsmodels
===========

Installed: 0.11.0 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\statsmodels)

Required Dependencies
=====================

cython: 0.29.15 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\Cython)
numpy: 1.18.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\numpy)
scipy: 1.4.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\scipy)
pandas: 1.0.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\pandas)
    dateutil: 2.8.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\dateutil)
patsy: 0.5.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\patsy)

Optional Dependencies
=====================

matplotlib: 3.1.3 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\matplotlib)
    backend: module://ipykernel.pylab.backend_inline 
cvxopt: Not installed
joblib: 0.14.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\joblib)

Developer Tools
================

IPython: 7.12.0 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\IPython)
    jinja2: 2.11.1 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\jinja2)
sphinx: 2.4.0 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\sphinx)
    pygments: 2.5.2 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\pygments)
pytest: 5.3.5 (D:\ProgramData\Anaconda3\envs\tfnightly\lib\site-packages\pytest)
virtualenv: Not installed

</details>



Discussion:
Thanks, it looks like we just need to include an `asarray` somewhere.

Resolving patch:
diff --git a/statsmodels/regression/linear_model.py b/statsmodels/regression/linear_model.py
--- a/statsmodels/regression/linear_model.py
+++ b/statsmodels/regression/linear_model.py
@@ -615,6 +615,8 @@ def hessian_factor(self, params, scale=None, observed=True):
     def fit_regularized(self, method="elastic_net", alpha=0.,
                         L1_wt=1., start_params=None, profile_scale=False,
                         refit=False, **kwargs):
+        if not np.isscalar(alpha):
+            alpha = np.asarray(alpha)
         # Need to adjust since RSS/n term in elastic net uses nominal
         # n in denominator
         if self.sigma is not None:
@@ -792,7 +794,8 @@ def fit_regularized(self, method="elastic_net", alpha=0.,
                         L1_wt=1., start_params=None, profile_scale=False,
                         refit=False, **kwargs):
         # Docstring attached below
-
+        if not np.isscalar(alpha):
+            alpha = np.asarray(alpha)
         # Need to adjust since RSS/n in elastic net uses nominal n in
         # denominator
         alpha = alpha * np.sum(self.weights) / len(self.weights)
@@ -1140,6 +1143,7 @@ def _fit_ridge(self, alpha):
             params = q / sd
             params = np.dot(v, params)
         else:
+            alpha = np.asarray(alpha)
             vtav = self.nobs * np.dot(vt, alpha[:, None] * v)
             d = np.diag(vtav) + s2
             np.fill_diagonal(vtav, d)


Test patch:
diff --git a/statsmodels/regression/tests/test_regression.py b/statsmodels/regression/tests/test_regression.py
--- a/statsmodels/regression/tests/test_regression.py
+++ b/statsmodels/regression/tests/test_regression.py
@@ -1107,6 +1107,39 @@ def test_regularized_weights(self):
                 assert_almost_equal(rslt1.params, rslt2.params, decimal=3)
                 assert_almost_equal(rslt1.params, rslt3.params, decimal=3)
 
+    def test_regularized_weights_list(self):
+
+        np.random.seed(132)
+        exog1 = np.random.normal(size=(100, 3))
+        endog1 = exog1[:, 0] + exog1[:, 1] + np.random.normal(size=100)
+        exog2 = np.random.normal(size=(100, 3))
+        endog2 = exog2[:, 0] + exog2[:, 1] + np.random.normal(size=100)
+
+        exog_a = np.vstack((exog1, exog1, exog2))
+        endog_a = np.concatenate((endog1, endog1, endog2))
+
+        # Should be equivalent to exog_a, endog_a.
+        exog_b = np.vstack((exog1, exog2))
+        endog_b = np.concatenate((endog1, endog2))
+        wgts = np.ones(200)
+        wgts[0:100] = 2
+        sigma = np.diag(1/wgts)
+
+        for L1_wt in 0, 0.5, 1:
+            for alpha_element in 0, 1:
+                alpha = [alpha_element,] * 3
+
+                mod1 = OLS(endog_a, exog_a)
+                rslt1 = mod1.fit_regularized(L1_wt=L1_wt, alpha=alpha)
+
+                mod2 = WLS(endog_b, exog_b, weights=wgts)
+                rslt2 = mod2.fit_regularized(L1_wt=L1_wt, alpha=alpha)
+
+                mod3 = GLS(endog_b, exog_b, sigma=sigma)
+                rslt3 = mod3.fit_regularized(L1_wt=L1_wt, alpha=alpha)
+
+                assert_almost_equal(rslt1.params, rslt2.params, decimal=3)
+                assert_almost_equal(rslt1.params, rslt3.params, decimal=3)
 
 def test_formula_missing_cat():
     # gh-805