Repository: modin-project/modin

Issue:
BUG: `set_index` works wrong with multiindex
### Modin version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the latest released version of Modin.

- [X] I have confirmed this bug exists on the main branch of Modin. (In order to do this you can follow [this guide](https://modin.readthedocs.io/en/stable/getting_started/installation.html#installing-from-the-github-master-branch).)


### Reproducible Example

```python
import modin.pandas as pd
import numpy as np

import pdb;pdb.set_trace()
df = pd.DataFrame(np.ones([2,4]),columns = [['a','b','c','d'],['','','x','y']])

print(df.set_index('a'))
print(df._to_pandas().set_index('a'))
```


### Issue Description

Wrong output from the first print:
```python
          a    b    c    d
                    x    y
(d, y)
1.0     1.0  1.0  1.0  1.0
1.0     1.0  1.0  1.0  1.0
```

### Expected Behavior

Expected output (from the second print):
```python
       b    c    d
            x    y
a
1.0  1.0  1.0  1.0
1.0  1.0  1.0  1.0
```

### Error Logs

<details>

```python-traceback

Replace this line with the error backtrace (if applicable).

```

</details>


### Installed Versions

<details>

Replace this line with the output of pd.show_versions()

</details>



Discussion:

Resolving patch:
diff --git a/modin/core/dataframe/pandas/dataframe/dataframe.py b/modin/core/dataframe/pandas/dataframe/dataframe.py
--- a/modin/core/dataframe/pandas/dataframe/dataframe.py
+++ b/modin/core/dataframe/pandas/dataframe/dataframe.py
@@ -652,11 +652,31 @@ def take_2d_labels_or_positional(
 
         if row_labels is not None:
             # Get numpy array of positions of values from `row_labels`
-            row_positions = self.index.get_indexer_for(row_labels)
+            if isinstance(self.index, pandas.MultiIndex):
+                row_positions = np.zeros(len(row_labels), dtype="int64")
+                # we can't use .get_locs(row_labels) because the function
+                # requires a different format for row_labels
+                for idx, label in enumerate(row_labels):
+                    if isinstance(label, str):
+                        label = [label]
+                    # get_loc can return slice that _take_2d_positional can't handle
+                    row_positions[idx] = self.index.get_locs(label)[0]
+            else:
+                row_positions = self.index.get_indexer_for(row_labels)
 
         if col_labels is not None:
             # Get numpy array of positions of values from `col_labels`
-            col_positions = self.columns.get_indexer_for(col_labels)
+            if isinstance(self.columns, pandas.MultiIndex):
+                col_positions = np.zeros(len(col_labels), dtype="int64")
+                # we can't use .get_locs(col_labels) because the function
+                # requires a different format for row_labels
+                for idx, label in enumerate(col_labels):
+                    if isinstance(label, str):
+                        label = [label]
+                    # get_loc can return slice that _take_2d_positional can't handle
+                    col_positions[idx] = self.columns.get_locs(label)[0]
+            else:
+                col_positions = self.columns.get_indexer_for(col_labels)
 
         return self._take_2d_positional(row_positions, col_positions)
 
@@ -1041,12 +1061,17 @@ def to_labels(self, column_list: List[Hashable]) -> "PandasDataframe":
         extracted_columns = self.take_2d_labels_or_positional(
             col_labels=column_list
         ).to_pandas()
+
         if len(column_list) == 1:
-            new_labels = pandas.Index(extracted_columns.squeeze(axis=1))
+            new_labels = pandas.Index(
+                extracted_columns.squeeze(axis=1), name=column_list[0]
+            )
         else:
-            new_labels = pandas.MultiIndex.from_frame(extracted_columns)
+            new_labels = pandas.MultiIndex.from_frame(
+                extracted_columns, names=column_list
+            )
         result = self.take_2d_labels_or_positional(
-            col_labels=[i for i in self.columns if i not in column_list]
+            col_labels=[i for i in self.columns if i not in extracted_columns.columns]
         )
         result.index = new_labels
         return result


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
@@ -307,6 +307,14 @@ def test_set_index(data):
     eval_general(modin_df, pandas_df, lambda df: df.set_index("inexistent_col"))
 
 
+@pytest.mark.parametrize("index", ["a", ["a", ("b", "")]])
+def test_set_index_with_multiindex(index):
+    # see #5186 for details
+    kwargs = {"columns": [["a", "b", "c", "d"], ["", "", "x", "y"]]}
+    modin_df, pandas_df = create_test_dfs(np.random.rand(2, 4), **kwargs)
+    eval_general(modin_df, pandas_df, lambda df: df.set_index(index))
+
+
 @pytest.mark.gpu
 @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
 def test_keys(data):
diff --git a/modin/test/storage_formats/pandas/test_internals.py b/modin/test/storage_formats/pandas/test_internals.py
--- a/modin/test/storage_formats/pandas/test_internals.py
+++ b/modin/test/storage_formats/pandas/test_internals.py
@@ -158,6 +158,27 @@ def test_aligning_partitions():
     repr(modin_df2)
 
 
+@pytest.mark.parametrize("row_labels", [None, [("a", "")], ["a"]])
+@pytest.mark.parametrize("col_labels", [None, ["a1"], [("c1", "z")]])
+def test_take_2d_labels_or_positional(row_labels, col_labels):
+    kwargs = {
+        "index": [["a", "b", "c", "d"], ["", "", "x", "y"]],
+        "columns": [["a1", "b1", "c1", "d1"], ["", "", "z", "x"]],
+    }
+    md_df, pd_df = create_test_dfs(np.random.rand(4, 4), **kwargs)
+
+    _row_labels = slice(None) if row_labels is None else row_labels
+    _col_labels = slice(None) if col_labels is None else col_labels
+    pd_df = pd_df.loc[_row_labels, _col_labels]
+    modin_frame = md_df._query_compiler._modin_frame
+    new_modin_frame = modin_frame.take_2d_labels_or_positional(
+        row_labels=row_labels, col_labels=col_labels
+    )
+    md_df._query_compiler._modin_frame = new_modin_frame
+
+    df_equals(md_df, pd_df)
+
+
 @pytest.mark.parametrize("has_partitions_shape_cache", [True, False])
 @pytest.mark.parametrize("has_frame_shape_cache", [True, False])
 def test_apply_func_to_both_axis(has_partitions_shape_cache, has_frame_shape_cache):