Repository: statsmodels/statsmodels

Issue:
ENH: `impulse_responses` in `VARMAX` the way of passing impulse
In `VARMAX`, I think there is room to improve the way of passing the impulse as an argument of `impulse_responses`.

Currently, `impulse_responses` without an impulse parameter implicitly set the impulse to the first column of an array or a `pd.DataFrame`. 

But users who use `pd.DataFrame` may want to choose to which column they set the impulse by column name.

I want to know your opinion on this issue. 


Discussion:

Resolving patch:
diff --git a/statsmodels/tsa/statespace/mlemodel.py b/statsmodels/tsa/statespace/mlemodel.py
--- a/statsmodels/tsa/statespace/mlemodel.py
+++ b/statsmodels/tsa/statespace/mlemodel.py
@@ -2021,10 +2021,12 @@ def impulse_responses(self, params, steps=1, impulse=0,
             Default is 1. Note that for time-invariant models, the initial
             impulse is not counted as a step, so if `steps=1`, the output will
             have 2 entries.
-        impulse : int or array_like
+        impulse : int, str or array_like
             If an integer, the state innovation to pulse; must be between 0
-            and `k_posdef-1`. Alternatively, a custom impulse vector may be
-            provided; must be shaped `k_posdef x 1`.
+            and `k_posdef-1`. If a str, it indicates which column of df
+            the unit (1) impulse is given.
+            Alternatively, a custom impulse vector may be provided; must be
+            shaped `k_posdef x 1`.
         orthogonalized : bool, optional
             Whether or not to perform impulse using orthogonalized innovations.
             Note that this will also affect custum `impulse` vectors. Default
@@ -2164,6 +2166,14 @@ def impulse_responses(self, params, steps=1, impulse=0,
                                     start=iloc, end=end, **kwargs)
 
         # Compute the impulse responses
+
+        # Convert endog name to index
+        use_pandas = isinstance(self.data, PandasData)
+        if type(impulse) == str:
+            if not use_pandas:
+                raise ValueError('Endog must be pd.DataFrame.')
+            impulse = self.endog_names.index(impulse)
+
         irfs = sim_model.impulse_responses(
             steps, impulse, orthogonalized, cumulative)
 
@@ -2172,7 +2182,6 @@ def impulse_responses(self, params, steps=1, impulse=0,
             irfs = irfs[:, 0]
 
         # Wrap data / squeeze where appropriate
-        use_pandas = isinstance(self.data, PandasData)
         if use_pandas:
             if self.k_endog == 1:
                 irfs = pd.Series(irfs, name=self.endog_names)
@@ -3490,10 +3499,12 @@ def impulse_responses(self, steps=1, impulse=0, orthogonalized=False,
             Default is 1. Note that for time-invariant models, the initial
             impulse is not counted as a step, so if `steps=1`, the output will
             have 2 entries.
-        impulse : int or array_like
+        impulse : int, str or array_like
             If an integer, the state innovation to pulse; must be between 0
-            and `k_posdef-1`. Alternatively, a custom impulse vector may be
-            provided; must be shaped `k_posdef x 1`.
+            and `k_posdef-1`. If a str, it indicates which column of df
+            the unit (1) impulse is given.
+            Alternatively, a custom impulse vector may be provided; must be
+            shaped `k_posdef x 1`.
         orthogonalized : bool, optional
             Whether or not to perform impulse using orthogonalized innovations.
             Note that this will also affect custum `impulse` vectors. Default


Test patch:
diff --git a/statsmodels/tsa/statespace/tests/test_impulse_responses.py b/statsmodels/tsa/statespace/tests/test_impulse_responses.py
--- a/statsmodels/tsa/statespace/tests/test_impulse_responses.py
+++ b/statsmodels/tsa/statespace/tests/test_impulse_responses.py
@@ -15,6 +15,7 @@
 from statsmodels.tools.sm_exceptions import EstimationWarning
 from statsmodels.tsa.statespace import (mlemodel, sarimax, structural, varmax,
                                         dynamic_factor)
+from statsmodels.tsa.vector_ar.tests.test_var import get_macrodata
 
 
 def test_sarimax():
@@ -269,6 +270,26 @@ def test_varmax():
                                    orthogonalized=True)
     assert_allclose(actual, np.c_[oirf_10, oirf_11], atol=1e-6)
 
+    # Impulse response passing column name
+    data = get_macrodata().view((float, 3), type=np.ndarray)
+
+    df = pd.DataFrame({
+        "a": data[:, 0],
+        "b": data[:, 1],
+        "c": data[:, 2]})
+
+    mod1 = varmax.VARMAX(df, order=(1, 0), trend='c')
+    mod1_result = mod1.fit()
+    mod2 = varmax.VARMAX(data, order=(1, 0), trend='c')
+    mod2_result = mod2.fit()
+
+    with pytest.raises(ValueError, match='Endog must be pd.DataFrame.'):
+        mod2_result.impulse_responses(6, impulse="b")
+
+    response1 = mod1_result.impulse_responses(6, impulse="b")
+    response2 = mod1_result.impulse_responses(6, impulse=[0, 1, 0])
+    assert_allclose(response1, response2)
+
     # VARMA(2, 2) + trend + exog
     # TODO: This is just a smoke test
     with warnings.catch_warnings():