Repository: modin-project/modin

Issue:
REFACTOR: check number of levels in index with nlevels instead of looking for pd.MultiIndex
porting https://github.com/ponder-org/modin-public/commit/40932f3586270454f0512a20cc41ecda9b3b4f04


Discussion:

Resolving patch:
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -1506,10 +1506,10 @@ def unstack(self, level=-1, fill_value=None):  # noqa: PR01, RT01, D200
         """
         Pivot a level of the (necessarily hierarchical) index labels.
         """
-        if not isinstance(self.index, pandas.MultiIndex) or (
-            isinstance(self.index, pandas.MultiIndex)
-            and is_list_like(level)
-            and len(level) == self.index.nlevels
+        # This ensures that non-pandas MultiIndex objects are caught.
+        is_multiindex = len(self.index.names) > 1
+        if not is_multiindex or (
+            is_multiindex and is_list_like(level) and len(level) == self.index.nlevels
         ):
             return self._reduce_dimension(
                 query_compiler=self._query_compiler.unstack(level, fill_value)
@@ -2026,10 +2026,10 @@ def stack(self, level=-1, dropna=True):  # noqa: PR01, RT01, D200
         """
         Stack the prescribed level(s) from columns to index.
         """
-        if not isinstance(self.columns, pandas.MultiIndex) or (
-            isinstance(self.columns, pandas.MultiIndex)
-            and is_list_like(level)
-            and len(level) == self.columns.nlevels
+        # This ensures that non-pandas MultiIndex objects are caught.
+        is_multiindex = len(self.columns.names) > 1
+        if not is_multiindex or (
+            is_multiindex and is_list_like(level) and len(level) == self.columns.nlevels
         ):
             return self._reduce_dimension(
                 query_compiler=self._query_compiler.stack(level, dropna)
diff --git a/modin/pandas/series.py b/modin/pandas/series.py
--- a/modin/pandas/series.py
+++ b/modin/pandas/series.py
@@ -1425,9 +1425,15 @@ def unstack(self, level=-1, fill_value=None):  # noqa: PR01, RT01, D200
         """
         from .dataframe import DataFrame
 
-        result = DataFrame(
-            query_compiler=self._query_compiler.unstack(level, fill_value)
-        )
+        # We can't unstack a Series object, if we don't have a MultiIndex.
+        if len(self.index.names) > 1:
+            result = DataFrame(
+                query_compiler=self._query_compiler.unstack(level, fill_value)
+            )
+        else:
+            raise ValueError(
+                f"index must be a MultiIndex to unstack, {type(self.index)} was passed"
+            )
 
         return result.droplevel(0, axis=1) if result.columns.nlevels > 1 else result
 


Test patch:
diff --git a/modin/pandas/test/test_series.py b/modin/pandas/test/test_series.py
--- a/modin/pandas/test/test_series.py
+++ b/modin/pandas/test/test_series.py
@@ -2303,7 +2303,7 @@ def test_loc(data):
     data = np.arange(100)
     modin_series = pd.Series(data, index=index).sort_index()
     pandas_series = pandas.Series(data, index=index).sort_index()
-    # Using 'fmt: skip' below as 'black' and 'flake8' can't agree on how this should be formatted
+
     modin_result = modin_series.loc[
         (slice(None), 1),
     ]  # fmt: skip
@@ -3650,6 +3650,12 @@ def test_unstack(data):
     )
 
 
+def test_unstack_error_no_multiindex():
+    modin_series = pd.Series([0, 1, 2])
+    with pytest.raises(ValueError, match="index must be a MultiIndex to unstack"):
+        modin_series.unstack()
+
+
 @pytest.mark.parametrize(
     "data, other_data",
     [([1, 2, 3], [4, 5, 6]), ([1, 2, 3], [4, 5, 6, 7, 8]), ([1, 2, 3], [4, np.nan, 6])],