Repository: modin-project/modin

Issue:
modin DataFrame does not qualify as DataFrame in pd.MultiIndex.from_frame
### System information
- **OS Platform and Distribution**: Linux Ubuntu 16.04
- **Modin version**: 0.8.2
- **Python version**: 3.8.2
- **Code we can use to reproduce**:

```python
from modin import pandas as pd
df = pd.DataFrame(
     [["bar", 1], ["bar", 2], ["foo", 1], ["foo", 2]],
     columns=["first", "second"],
)
print(type(df))
pd.MultiIndex.from_frame(df)
```

### Problem
When trying to look at the indices of the DataFrame created as above the type of the DataFrame is not recognized.

So, with

```python
print(type(df))
# <class 'modin.pandas.dataframe.DataFrame'>
```

the output of the following 

```python
pd.MultiIndex.from_frame(df)
```

returns

```
TypeError: Input must be a DataFrame
```

### Source code / logs

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/myscript.py in <module>
      4      columns=["first", "second"],
      5 )
----> 6 pd.MultiIndex.from_frame(df)

~/somepath/.venv/lib/python3.8/site-packages/pandas/core/indexes/multi.py in from_frame(cls, df, sortorder, names)
    626         """
    627         if not isinstance(df, ABCDataFrame):
--> 628             raise TypeError("Input must be a DataFrame")
    629 
    630         column_names, columns = zip(*df.items())
TypeError: Input must be a DataFrame
```



Discussion:
Yes, we currently reuse Pandas class `MultiIndex` as is, and therefore its method `from_frame` expects a Pandas DataFrame type, not Modin. As a temporary workaround you can write `pd.MultiIndex.from_frame(df._to_pandas())` and it should work.


Resolving patch:
diff --git a/modin/pandas/utils.py b/modin/pandas/utils.py
--- a/modin/pandas/utils.py
+++ b/modin/pandas/utils.py
@@ -13,6 +13,8 @@
 
 """Implement utils for pandas component."""
 
+from pandas import MultiIndex
+
 
 def from_non_pandas(df, index, columns, dtype):
     """
@@ -105,3 +107,38 @@ def is_scalar(obj):
     from .base import BasePandasDataset
 
     return not isinstance(obj, BasePandasDataset) and pandas_is_scalar(obj)
+
+
+def from_modin_frame_to_mi(df, sortorder=None, names=None):
+    """
+    Make a pandas.MultiIndex from a DataFrame.
+
+    Parameters
+    ----------
+    df : DataFrame
+        DataFrame to be converted to pandas.MultiIndex.
+    sortorder : int, optional
+        Level of sortedness (must be lexicographically sorted by that
+        level).
+    names : list-like, optional
+        If no names are provided, use the column names, or tuple of column
+        names if the columns is a MultiIndex. If a sequence, overwrite
+        names with the given sequence.
+
+    Returns
+    -------
+    pandas.MultiIndex
+        The pandas.MultiIndex representation of the given DataFrame.
+    """
+    from .dataframe import DataFrame
+
+    if isinstance(df, DataFrame):
+        from modin.error_message import ErrorMessage
+
+        ErrorMessage.default_to_pandas("`MultiIndex.from_frame`")
+        df = df._to_pandas()
+    return _original_pandas_MultiIndex_from_frame(df, sortorder, names)
+
+
+_original_pandas_MultiIndex_from_frame = MultiIndex.from_frame
+MultiIndex.from_frame = from_modin_frame_to_mi


Test patch:
diff --git a/modin/pandas/test/dataframe/test_indexing.py b/modin/pandas/test/dataframe/test_indexing.py
--- a/modin/pandas/test/dataframe/test_indexing.py
+++ b/modin/pandas/test/dataframe/test_indexing.py
@@ -1325,3 +1325,17 @@ def test_index_order():
             getattr(df_modin, func)(level=0).index,
             getattr(df_pandas, func)(level=0).index,
         )
+
+
+@pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
+@pytest.mark.parametrize("sortorder", [0, 3, 5])
+def test_multiindex_from_frame(data, sortorder):
+    modin_df, pandas_df = create_test_dfs(data)
+
+    def call_from_frame(df):
+        if type(df).__module__.startswith("pandas"):
+            return pandas.MultiIndex.from_frame(df, sortorder)
+        else:
+            return pd.MultiIndex.from_frame(df, sortorder)
+
+    eval_general(modin_df, pandas_df, call_from_frame, comparator=assert_index_equal)