Repository: statsmodels/statsmodels

Issue:
DiscreteMargins.summary_frame() fails with *** ValueError: all the input arrays must have same number of dimensions
summary_frame() of a MNLogit estimate is failing for me, because the dimensions of self.conf_int(alpha) are twice the size of everything else in the following list of arrays:

```
        table = np.column_stack((self.margeff, self.margeff_se, self.tvalues,
                                 self.pvalues, self.conf_int(alpha) ))
```



Discussion:
@cpbl Thanks for reporting, I never looked at this, we have some problems because the params in MNLogit are 2 dimensional, while most of the generic structure is designed for 1-dimensional params.

Do margeff and margeff_se itself look ok?

They look just like what I wanted them to show! so probably good. I haven't compared the s.e.'s to another package/software.  
Yes, is the way to fix this just to test self.margeff.ndim, and behave differently in the two cases? 
The problem is that I'm not sure we have access here to the different categorical values of the MNLogit, which ought really to be in the DataFrame (?)
margeff and margeff_se are unit tested, against Stata 11 AFAICS
so this is most likely in conf_int which doesn't have unit tests in the main CheckMNLogitBaseZero.
I don't know if there are generic tests for it.
Or maybe it's just a missing reshape in summary.

I just saw your comment appear.
I don't know yet where the special code for MNLogit 2d handling is in the margin code.
I will try to dig in with the Ames example from the unit tests, since know I don't know the shape of any of these extra results for MNLogit. The unit tests use ravel by column stacking, e.g.
`self.pvalues = np.reshape(pvalues, (6,-1), order='F')`

looks like the problems with the 3-d conf_int are confined to the summaries
`summary()` gives me only a single column for the confint which looks like the lower bound
summary_frame might also have an index error

summary is looping over the multivariate endog, but summary_frame does not have the extra loop.
Is there supposed to be a hierarchical/multi index for the dataFrame with (endog_name, exog_name)?
Then, we could just ravel/reshape the numbers for the table and create the double index.
one down: 
summary is a refactoring victim when we split the columns of the conf_int.
margin header has one column less
```
header = ['', _transform_names[method], 'std err', 'z',
                        'P>|z|', '[' + str(alpha/2), str(1-alpha/2) + ']'] #'[%3.1f%% Conf. Int.]' % (100-alpha*100)]
```
I have something like this to generate the DataFrame in  DiscreteMargins.summary_frame()
```
        if self.margeff.ndim==2:
            table2 = np.dstack([self.margeff, self.margeff_se, self.tvalues, self.pvalues, self.conf_int(alpha)[:,0,:],self.conf_int(alpha)[:,1,:]])         
            m,n,r = table2.shape # m=len(var_names), n=len(yvals), r=len(cols, ie names)
            out_arr = np.column_stack((np.repeat(np.array(var_names),n),np.array(list(range(n))*m),table2.reshape(m*n,-1)))
            out_df = pd.DataFrame(out_arr)
            out_df.columns=['x','ycategory']+names
```
```
>>> table = np.column_stack([i.ravel("F") for i in [self.margeff, self.margeff_se, self.tvalues,
                                 self.pvalues, self.conf_int(alpha)[:, 0, :], self.conf_int(alpha)[:, 1, :]]])
>>> table.shape
(35, 6)
```
this creates a numpy array for the dataframe with the params table columns, and rows are stacked equation by equation, like in the printed summary table

@cpbl 

Does stacking with multi-index look fine to you?
I'm not really a pandas expert. This is what I'm experimenting with right now

```
>>> print(marge_df)
               dy/dx  Std. Err.          z      Pr(>|z|)  Conf. Int. Low  Cont. Int. Hi.
endog exog                                                                              
y=0   x1    0.008681   0.003858   2.250031  2.444696e-02        0.001119        0.016243
      x2   -0.097799   0.008047 -12.153246  5.513267e-34       -0.113571       -0.082026
      x3    0.002726   0.000707   3.855944  1.152837e-04        0.001340        0.004111
      x4   -0.019924   0.008232  -2.420313  1.550716e-02       -0.036058       -0.003790
      x5   -0.006031   0.002026  -2.976715  2.913545e-03       -0.010003       -0.002060
      x1    0.006994   0.003904   1.791288  7.324701e-02       -0.000659        0.014646
y=1   x2   -0.050224   0.007360  -6.823928  8.858411e-12       -0.064650       -0.035799
      x3   -0.002110   0.000756  -2.789467  5.279489e-03       -0.003593       -0.000627
      x4   -0.005370   0.008438  -0.636371  5.245346e-01       -0.021908        0.011169
      x5   -0.005544   0.002048  -2.706573  6.798162e-03       -0.009558       -0.001529
      x1   -0.003910   0.003137  -1.246494  2.125831e-01       -0.010059        0.002238
      x2   -0.028247   0.005681  -4.971940  6.628628e-07       -0.039382       -0.017112
y=2   x3   -0.001006   0.000660  -1.523187  1.277120e-01       -0.002299        0.000288
      x4    0.006643   0.006893   0.963750  3.351713e-01       -0.006867        0.020154
      x5    0.000980   0.001848   0.530183  5.959852e-01       -0.002642        0.004602
      x1   -0.001826   0.001943  -0.939793  3.473239e-01       -0.005634        0.001982
      x2   -0.005737   0.003190  -1.798305  7.212875e-02       -0.011991        0.000516
      x3   -0.000042   0.000387  -0.109930  9.124647e-01       -0.000800        0.000715
y=3   x4   -0.005467   0.004362  -1.253315  2.100911e-01       -0.014016        0.003082
      x5    0.000541   0.001152   0.469496  6.387148e-01       -0.001717        0.002800
      x1   -0.000986   0.002986  -0.330032  7.413761e-01       -0.006839        0.004868
      x2    0.019856   0.005408   3.671771  2.408754e-04        0.009257        0.030454
      x3    0.000480   0.000589   0.814938  4.151076e-01       -0.000674        0.001633
      x4    0.001726   0.006443   0.267912  7.887674e-01       -0.010901        0.014353
y=4   x5    0.002113   0.001889   1.118724  2.632579e-01       -0.001589        0.005815
      x1   -0.001535   0.003581  -0.428607  6.682090e-01       -0.008553        0.005483
      x2    0.037553   0.006950   5.403581  6.532362e-08        0.023932        0.051175
      x3   -0.000685   0.000723  -0.948514  3.428680e-01       -0.002101        0.000731
      x4    0.004725   0.007829   0.603507  5.461716e-01       -0.010619        0.020069
      x5    0.002547   0.002235   1.139625  2.544426e-01       -0.001834        0.006928
y=5   x1   -0.007418   0.003364  -2.205077  2.744870e-02       -0.014012       -0.000825
      x2    0.124599   0.008377  14.874540  4.822870e-50        0.108181        0.141016
      x3    0.000638   0.000677   0.941834  3.462778e-01       -0.000690        0.001966
      x4    0.017666   0.007351   2.403379  1.624432e-02        0.003259        0.032073
      x5    0.005394   0.002166   2.490234  1.276592e-02        0.001149        0.009639

>>> marge_df.index
MultiIndex(levels=[['y=0', 'y=1', 'y=2', 'y=3', 'y=4', 'y=5'], ['x1', 'x2', 'x3', 'x4', 'x5']],
           labels=[[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5], [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4]],
           names=['endog', 'exog'])
>>> 
```

compared to plain summary

```
>>> print(marg.summary())
       MNLogit Marginal Effects      
=====================================
Dep. Variable:                      y
Method:                          dydx
At:                           overall
==============================================================================
       y=0      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1             0.0087      0.004      2.250      0.024       0.001       0.016
x2            -0.0978      0.008    -12.153      0.000      -0.114      -0.082
x3             0.0027      0.001      3.856      0.000       0.001       0.004
x4            -0.0199      0.008     -2.420      0.016      -0.036      -0.004
x5            -0.0060      0.002     -2.977      0.003      -0.010      -0.002
------------------------------------------------------------------------------
       y=1      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1             0.0070      0.004      1.791      0.073      -0.001       0.015
x2            -0.0502      0.007     -6.824      0.000      -0.065      -0.036
x3            -0.0021      0.001     -2.789      0.005      -0.004      -0.001
x4            -0.0054      0.008     -0.636      0.525      -0.022       0.011
x5            -0.0055      0.002     -2.707      0.007      -0.010      -0.002
------------------------------------------------------------------------------
       y=2      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1            -0.0039      0.003     -1.246      0.213      -0.010       0.002
x2            -0.0282      0.006     -4.972      0.000      -0.039      -0.017
x3            -0.0010      0.001     -1.523      0.128      -0.002       0.000
x4             0.0066      0.007      0.964      0.335      -0.007       0.020
x5             0.0010      0.002      0.530      0.596      -0.003       0.005
------------------------------------------------------------------------------
       y=3      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1            -0.0018      0.002     -0.940      0.347      -0.006       0.002
x2            -0.0057      0.003     -1.798      0.072      -0.012       0.001
x3         -4.249e-05      0.000     -0.110      0.912      -0.001       0.001
x4            -0.0055      0.004     -1.253      0.210      -0.014       0.003
x5             0.0005      0.001      0.469      0.639      -0.002       0.003
------------------------------------------------------------------------------
       y=4      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1            -0.0010      0.003     -0.330      0.741      -0.007       0.005
x2             0.0199      0.005      3.672      0.000       0.009       0.030
x3             0.0005      0.001      0.815      0.415      -0.001       0.002
x4             0.0017      0.006      0.268      0.789      -0.011       0.014
x5             0.0021      0.002      1.119      0.263      -0.002       0.006
------------------------------------------------------------------------------
       y=5      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1            -0.0015      0.004     -0.429      0.668      -0.009       0.005
x2             0.0376      0.007      5.404      0.000       0.024       0.051
x3            -0.0007      0.001     -0.949      0.343      -0.002       0.001
x4             0.0047      0.008      0.604      0.546      -0.011       0.020
x5             0.0025      0.002      1.140      0.254      -0.002       0.007
------------------------------------------------------------------------------
       y=6      dy/dx    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1            -0.0074      0.003     -2.205      0.027      -0.014      -0.001
x2             0.1246      0.008     14.875      0.000       0.108       0.141
x3             0.0006      0.001      0.942      0.346      -0.001       0.002
x4             0.0177      0.007      2.403      0.016       0.003       0.032
x5             0.0054      0.002      2.490      0.013       0.001       0.010
==============================================================================
```
messages just crossed.

I had the column_stacking like you have before, but I think the unraveled row stacking might be more useful.
@josef-pkt Great!  
I should have just had dinner while waiting for the expert to do it properly!
Thanks for offering pandas output in these things.

using the multiindex, as far as I can figure it out

```
>>> marge_df.loc[("y=1", ), ["Conf. Int. Low", "Cont. Int. Hi."]]
      Conf. Int. Low  Cont. Int. Hi.
exog                                
x1         -0.000659        0.014646
x2         -0.064650       -0.035799
x3         -0.003593       -0.000627
x4         -0.021908        0.011169
x5         -0.009558       -0.001529

>>> marg.conf_int()[:,:,1]
array([[-0.00065857,  0.01464631],
       [-0.0646497 , -0.03579892],
       [-0.00359262, -0.00062746],
       [-0.02190829,  0.01116869],
       [-0.00955811, -0.00152922]])
```


Resolving patch:
diff --git a/statsmodels/discrete/discrete_margins.py b/statsmodels/discrete/discrete_margins.py
--- a/statsmodels/discrete/discrete_margins.py
+++ b/statsmodels/discrete/discrete_margins.py
@@ -446,18 +446,43 @@ def summary_frame(self, alpha=.05):
         -------
         frame : DataFrames
             A DataFrame summarizing the marginal effects.
+
+        Notes
+        -----
+        The dataframe is created on each call and not cached, as are the
+        tables build in `summary()`
         """
         _check_at_is_all(self.margeff_options)
-        from pandas import DataFrame
+        results = self.results
+        model = self.results.model
+        from pandas import DataFrame, MultiIndex
         names = [_transform_names[self.margeff_options['method']],
                                   'Std. Err.', 'z', 'Pr(>|z|)',
                                   'Conf. Int. Low', 'Cont. Int. Hi.']
         ind = self.results.model.exog.var(0) != 0 # True if not a constant
         exog_names = self.results.model.exog_names
         var_names = [name for i,name in enumerate(exog_names) if ind[i]]
-        table = np.column_stack((self.margeff, self.margeff_se, self.tvalues,
-                                 self.pvalues, self.conf_int(alpha)))
-        return DataFrame(table, columns=names, index=var_names)
+
+        if self.margeff.ndim == 2:
+            # MNLogit case
+            ci = self.conf_int(alpha)
+            table = np.column_stack([i.ravel("F") for i in
+                        [self.margeff, self.margeff_se, self.tvalues,
+                         self.pvalues, ci[:, 0, :], ci[:, 1, :]]])
+
+            _, yname_list = results._get_endog_name(model.endog_names,
+                                                        None, all=True)
+            ynames = np.repeat(yname_list, len(var_names))
+            xnames = np.tile(var_names, len(yname_list))
+            index = MultiIndex.from_tuples(list(zip(ynames, xnames)),
+                                           names=['endog', 'exog'])
+        else:
+            table = np.column_stack((self.margeff, self.margeff_se, self.tvalues,
+                                     self.pvalues, self.conf_int(alpha)))
+            index=var_names
+
+        return DataFrame(table, columns=names, index=index)
+
 
     @cache_readonly
     def pvalues(self):
@@ -551,7 +576,7 @@ def summary(self, alpha=.05):
                 tble.title = yname_list[eq]
                 # overwrite coef with method name
                 header = ['', _transform_names[method], 'std err', 'z',
-                        'P>|z|', '[%3.1f%% Conf. Int.]' % (100-alpha*100)]
+                        'P>|z|', '[' + str(alpha/2), str(1-alpha/2) + ']']
                 tble.insert_header_row(0, header)
                 #from IPython.core.debugger import Pdb; Pdb().set_trace()
                 table.append(tble)
@@ -562,7 +587,7 @@ def summary(self, alpha=.05):
             table = summary_params(restup, yname=yname, xname=exog_names,
                     alpha=alpha, use_t=False, skip_header=True)
             header = ['', _transform_names[method], 'std err', 'z',
-                        'P>|z|', '[%3.1f%% Conf. Int.]' % (100-alpha*100)]
+                        'P>|z|', '[' + str(alpha/2), str(1-alpha/2) + ']']
             table.insert_header_row(0, header)
 
         smry.tables.append(table)


Test patch:
diff --git a/statsmodels/discrete/tests/test_discrete.py b/statsmodels/discrete/tests/test_discrete.py
--- a/statsmodels/discrete/tests/test_discrete.py
+++ b/statsmodels/discrete/tests/test_discrete.py
@@ -145,6 +145,12 @@ def test_nodummy_dydxoverall(self):
         assert_almost_equal(me.margeff_se,
                 self.res2.margeff_nodummy_dydx_se, DECIMAL_4)
 
+        me_frame = me.summary_frame()
+        eff = me_frame["dy/dx"].values
+        assert_allclose(eff, me.margeff, rtol=1e-13)
+        assert_equal(me_frame.shape, (me.margeff.size, 6))
+
+
     def test_nodummy_dydxmean(self):
         me = self.res1.get_margeff(at='mean')
         assert_almost_equal(me.margeff,
@@ -1136,6 +1142,10 @@ def test_margeff_overall(self):
         me = self.res1.get_margeff()
         assert_almost_equal(me.margeff, self.res2.margeff_dydx_overall, 6)
         assert_almost_equal(me.margeff_se, self.res2.margeff_dydx_overall_se, 6)
+        me_frame = me.summary_frame()
+        eff = me_frame["dy/dx"].values.reshape(me.margeff.shape, order="F")
+        assert_allclose(eff, me.margeff, rtol=1e-13)
+        assert_equal(me_frame.shape, (np.size(me.margeff), 6))
 
     def test_margeff_mean(self):
         me = self.res1.get_margeff(at='mean')