Repository: statsmodels/statsmodels

Issue:
BUG: elasticnet uses transformed exposure
base.elasticnet does not use get_init_params and uses the attached log transformed exposure when recreating the model.

this is similar to #4597 fixed in #4601 
second bug is that binomial count stores transformed endog, that should be automatically fixed by #4601 which added n_trials to init_keys, see #4599.

```
    init_args = dict([(k, getattr(model, k)) for k in model._init_keys
                      if k != "offset" and hasattr(model, k)])
```

suggested fix:
check if either exposure or offset in init_keys, and then define
`model_offset = model.offset + np.exp(model.exposure)` (*)
replace `hasattr(model, "offset")` inside the loop with model_offset to avoid repeated lookup and transformation

needs unit tests

(*) for GLM we could use internal `model._offset_exposure` but that doesn't exist in discrete. It's better not to use any model specific attributes in base, unless we add the combined `model._offset_exposure` as convention for models that have offset and exposure


Discussion:
unit tests for exposure could just compare a GLM-Poisson using exposure with the model that uses `offset=np.log(exposure)`

@kshedden 

Resolving patch:
diff --git a/statsmodels/base/elastic_net.py b/statsmodels/base/elastic_net.py
--- a/statsmodels/base/elastic_net.py
+++ b/statsmodels/base/elastic_net.py
@@ -150,9 +150,15 @@ def fit_elasticnet(model, method="coord_descent", maxiter=100,
     btol = 1e-4
     params_zero = np.zeros(len(params), dtype=bool)
 
-    init_args = dict([(k, getattr(model, k)) for k in model._init_keys
-                      if k != "offset" and hasattr(model, k)])
+    init_args = model._get_init_kwds()
+    # we don't need a copy of init_args because get_init_kwds provides new dict
     init_args['hasconst'] = False
+    model_offset = init_args.pop('offset', None)
+    if 'exposure' in init_args and init_args['exposure'] is not None:
+        if model_offset is None:
+            model_offset = np.log(init_args.pop('exposure'))
+        else:
+            model_offset += np.log(init_args.pop('exposure'))
 
     fgh_list = [
         _gen_npfuncs(k, L1_wt, alpha, loglike_kwds, score_kwds, hess_kwds)
@@ -176,8 +182,8 @@ def fit_elasticnet(model, method="coord_descent", maxiter=100,
             params0 = params.copy()
             params0[k] = 0
             offset = np.dot(model.exog, params0)
-            if hasattr(model, "offset") and model.offset is not None:
-                offset += model.offset
+            if model_offset is not None:
+                offset += model_offset
 
             # Create a one-variable model for optimization.
             model_1var = model.__class__(


Test patch:
diff --git a/statsmodels/genmod/tests/test_glm.py b/statsmodels/genmod/tests/test_glm.py
--- a/statsmodels/genmod/tests/test_glm.py
+++ b/statsmodels/genmod/tests/test_glm.py
@@ -731,6 +731,11 @@ def test_offset_exposure(self):
         assert_allclose(mod3.exposure, mod1_.exposure, rtol=1e-14)
         assert_allclose(mod3.offset, mod1_.offset, rtol=1e-14)
 
+        # test fit_regularized exposure, see #4605
+        resr1 = mod1.model.fit_regularized()
+        resr2 = mod2.model.fit_regularized()
+        assert_allclose(resr1.params, resr2.params, rtol=1e-10)
+
 
     def test_predict(self):
         np.random.seed(382304)