Repository: statsmodels/statsmodels

Issue:
BUG: Init of MNLogit fails if endogenous variable is unnamed Pandas Series
#### Describe the bug

MNLogit throws a "TypeError: MultiIndex.name must be a hashable type" if the provided endogenous variable is a pandas Series whose `name` attribute is not set. The resulting stack trace is quite cryptic which makes it very hard for users to figure out what is going on.

Workaround: Make sure the Pandas Series is named.

#### Code Sample, a copy-pastable example if possible


```python
import pandas as pd
import statsmodels.api as sm


# This works fine
X = pd.DataFrame(data={"var1": [.1, .2, .3, .4, ], "var2": [.0, .1, .2, .3,]}, index = range(4))
y = pd.Series([2, 2, 1, 0, ], index=range(4), name="output")
sm.MNLogit(endog=y, exog=X).fit()

# This fails
X = pd.DataFrame(data={"var1": [.1, .2, .3, .4, ], "var2": [.0, .1, .2, .3,]}, index = range(4))
y = pd.Series([2, 2, 1, 0, ], index=range(4)) # <--- name attribute not set and defaults to None
sm.MNLogit(endog=y, exog=X).fit()
```
<details>

Here is the resulting stack trace. The issue is in happening [here](https://github.com/statsmodels/statsmodels/blob/ba8a0076ef9f464f0b7b3b432fdfad125dcdb12f/statsmodels/discrete/discrete_model.py#L2947). If `y.name` is None, the `yname` variable in the initializer takes a list as value for some reason. In consequence, the init of the Pandas Multiindex fails.
```
File "/Users/sven/.../statsmodels_mwe.py", line 13, in <module>
sm.MNLogit(endog=y, exog=X).fit()
File "/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/statsmodels/discrete/discrete_model.py", line 2270, in __init__
idx = MultiIndex.from_product((ynames[1:], self.data.xnames),
                              File "/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/pandas/core/indexes/multi.py", line 644, in from_product
return cls(levels, codes, sortorder=sortorder, names=names)
File "/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/pandas/core/indexes/multi.py", line 334, in __new__
result._set_names(names)
File "/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/pandas/core/indexes/multi.py", line 1489, in _set_names
raise TypeError(
    TypeError: MultiIndex.name must be a hashable type
```
</details>


~~If the issue has not been resolved, please file it in the issue tracker.~~ Don't know how to do that.

#### Expected Output

... it should not throw an error.

#### Output of ``import statsmodels.api as sm; sm.show_versions()``

<details>

INSTALLED VERSIONS
------------------
Python: 3.8.16.final.0
OS: Darwin 20.6.0 Darwin Kernel Version 20.6.0: Thu Sep 29 20:15:11 PDT 2022; root:xnu-7195.141.42~1/RELEASE_X86_64 x86_64
byteorder: little
LC_ALL: None
LANG: None
statsmodels
===========
Installed: 0.13.5 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/statsmodels)
Required Dependencies
=====================
cython: Not installed
numpy: 1.24.1 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/numpy)
scipy: 1.10.0 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/scipy)
pandas: 1.5.3 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/pandas)
    dateutil: 2.8.2 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/dateutil)
patsy: 0.5.3 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/patsy)
Optional Dependencies
=====================
matplotlib: 3.6.3 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/matplotlib)
Backend MacOSX is interactive backend. Turning interactive mode on.
    backend: MacOSX 
cvxopt: Not installed
joblib: 1.2.0 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/joblib)
Developer Tools
================
IPython: 8.8.0 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/IPython)
    jinja2: 3.1.2 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/jinja2)
sphinx: Not installed
    pygments: 2.14.0 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/pygments)
pytest: 7.2.1 (/Users/sven/miniconda3/envs/benjamin/lib/python3.8/site-packages/pytest)
virtualenv: Not installed
</details>



Discussion:
Thanks for reporting this.

It's a bug. 
But I don't know why it didn't show up in the past. That code should be unchanged for some time.

My guess is that somewhere in the datahandling there is no default yname "y" if there is no name provided.
This should be MNLogit or MultinomialModel specifc because models like OLS default to "y" as generic default name.

If endog is a  numpy array, it defaults to "y" as endog_names
The relevant code is in discrete_model

`_pandas_to_dummies` function which is called by `MultinomialModel._handle_data`

`get_dummies` is a pandas function, so we need to check how pandas creates default column names from a series with no name.

@svenbuechel  Thanks for reporting the bug

I will fix it today

Resolving patch:
diff --git a/statsmodels/discrete/discrete_model.py b/statsmodels/discrete/discrete_model.py
--- a/statsmodels/discrete/discrete_model.py
+++ b/statsmodels/discrete/discrete_model.py
@@ -136,11 +136,13 @@ def _pandas_to_dummies(endog):
         if endog.shape[1] == 1:
             yname = endog.columns[0]
             endog_dummies = get_dummies(endog.iloc[:, 0])
-        else:  # series
+        else:  # assume already dummies
             yname = 'y'
             endog_dummies = endog
     else:
         yname = endog.name
+        if yname is None:
+            yname = 'y'
         endog_dummies = get_dummies(endog)
     ynames = endog_dummies.columns.tolist()
 


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
@@ -1706,6 +1706,10 @@ def test_mnlogit_factor():
     params = res.params
     summary = res.summary()
     predicted = res.predict(exog.iloc[:5, :])
+    # check endog is series with no name #8672
+    endogn = dta['endog']
+    endogn.name = None
+    mod = sm.MNLogit(endogn, exog)
 
     # with patsy
     mod = smf.mnlogit('PID ~ ' + ' + '.join(dta.exog.columns), dta.data)