Repository: modin-project/modin

Issue:
Simple groupby causes TypeError
Here is a failing test:

```
import pandas as pd
import modin.pandas as mpd

data = {
    "a": [1, 1, 2],
    "b": [11, 11, 22],
    "c": [111, 111, 222]
}

df = pd.DataFrame(data)
df = pd.concat([df])
ref = df.groupby(["a", "b", df["c"]]).size()
print(ref)

df = mpd.DataFrame(data)
df = mpd.concat([df])
exp = df.groupby(["a", "b", df["c"]]).size()
print(exp)
```

The reason of fail is in DataFrame.groupby code. When we see that 'by' is a list, but it doesn't hold column labels only, we compare its length to index length. If lengths match, we assume, that 'by' holds key data, not a list of key columns/Series. In the test above this assumption is wrong.

Also, this length check triggers execution for lazy frame, which we want to avoid. So, we need to first check if 'by' is a list of key columns/Series and check index length only when we have to assume, that 'by' holds key data.


Discussion:
Is this an issue for OmniSci only? Please choose an appropriate label so we can classify this issue. Thanks!
This is a back-end agnostic issue. The problem is in front-end.
I see, thanks for clarifying. I have added to next release since there is not time to fix for this release.
I want to mention in advance, that while #1842 fixes error for this particular test case, it doesn't fix the original problem in the code. We can slightly change groupby statement to reproduce the original problem after #1842 is applied:

`df.groupby(["a", "b", df["c"] + 1]).size()`
@ienkovich Good catch, that example is significantly more complicated because the grouping values come from a transformation on the data rather than the data itself. We should track that particular case separately because it will require special care.
OK, I will open a separate issue for the additional case.

Resolving patch:
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -440,7 +440,21 @@ def groupby(
             by = by._query_compiler
         elif is_list_like(by):
             # fastpath for multi column groupby
-            if not isinstance(by, Series) and axis == 0 and all(o in self for o in by):
+            if (
+                not isinstance(by, Series)
+                and axis == 0
+                and all(
+                    (
+                        (isinstance(o, str) and (o in self))
+                        or (isinstance(o, Series) and (o._parent is self))
+                    )
+                    for o in by
+                )
+            ):
+                # We can just revert Series back to names because the parent is
+                # this dataframe:
+                by = [o.name if isinstance(o, Series) else o for o in by]
+
                 warnings.warn(
                     "Multi-column groupby is a new feature. "
                     "Please report any bugs/issues to bug_reports@modin.org."


Test patch:
diff --git a/modin/pandas/test/test_groupby.py b/modin/pandas/test/test_groupby.py
--- a/modin/pandas/test/test_groupby.py
+++ b/modin/pandas/test/test_groupby.py
@@ -1134,3 +1134,31 @@ def test_to_pandas_convertion(kwargs):
     by = ["a", "b"]
 
     eval_aggregation(*create_test_dfs(data), by=by, **kwargs)
+
+
+@pytest.mark.parametrize(
+    # When True, do df[name], otherwise just use name
+    "columns",
+    [
+        [(False, "a"), (False, "b"), (False, "c")],
+        [(False, "a"), (False, "b")],
+        [(True, "a"), (True, "b"), (True, "c")],
+        [(True, "a"), (True, "b")],
+        [(False, "a"), (False, "b"), (True, "c")],
+        [(False, "a"), (True, "c")],
+    ],
+)
+def test_mixed_columns(columns):
+    def get_columns(df):
+        return [df[name] if lookup else name for (lookup, name) in columns]
+
+    data = {"a": [1, 1, 2], "b": [11, 11, 22], "c": [111, 111, 222]}
+
+    df1 = pandas.DataFrame(data)
+    df1 = pandas.concat([df1])
+    ref = df1.groupby(get_columns(df1)).size()
+
+    df2 = pd.DataFrame(data)
+    df2 = pd.concat([df2])
+    exp = df2.groupby(get_columns(df2)).size()
+    df_equals(ref, exp)