Repository: modin-project/modin

Issue:
Column indexing is buggy when the dataframe has multiple columns having the same name
### Describe the problem

When a dataframe `df` looks like this:
```
   c1  c2  c1
0   1   4   7
1   2   5   8
2   3   6   9
```

`df.c1` should return a dataframe where the column `c1` appears twice. However, with Modin I got the following series as the output:

```
c1    1
c1    7
Name: 0, dtype: int64
```

The root cause is that `DataFrame._getitem_column` is blindly converting the query compiler output to a series even though the output may have more than one column.

### Source code / logs

https://github.com/modin-project/modin/blob/master/modin/pandas/dataframe.py#L1911


Discussion:
Thanks @magnatelee for the report, this is a bug as you say.

The difficulty with this is that we somehow need to communicate from the layers below the API what the return type is. Either we need to check the `len(_query_compiler.columns)` or we need to check the number of times the `key` is in the columns from the API layer.

Alternatively, and possibly most correctly, we could `squeeze` before returning. What do you think about this?
Calling `squeeze` sounds like the right thing to me as well, but I start to feel that forgetting whether a query compiler represents a series or a dataframe makes certain things somewhat difficult to handle (see my comment here: https://github.com/modin-project/modin/issues/1060#issuecomment-584269263). Would it be a bad idea to just remember this fact somewhere in the query compiler?
> Would it be a bad idea to just remember this fact somewhere in the query compiler?

I would have to think about this. I'm not entirely sure it's the best approach because it overfits to the pandas API, and we will want to integrate with other dataframe APIs in the future. I do see the issues related to this, though, because somehow we need to signal to the query compiler whether to broadcast a column to all columns or just do a join.

Resolving patch:
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -1908,8 +1908,11 @@ def _getitem(self, key):
     def _getitem_column(self, key):
         if key not in self.keys():
             raise KeyError("{}".format(key))
-        s = self._reduce_dimension(self._query_compiler.getitem_column_array([key]))
-        s._parent = self
+        s = DataFrame(
+            query_compiler=self._query_compiler.getitem_column_array([key])
+        ).squeeze(axis=1)
+        if isinstance(s, Series):
+            s._parent = self
         return s
 
     def _getitem_array(self, key):


Test patch:
diff --git a/modin/pandas/test/test_dataframe.py b/modin/pandas/test/test_dataframe.py
--- a/modin/pandas/test/test_dataframe.py
+++ b/modin/pandas/test/test_dataframe.py
@@ -5095,6 +5095,22 @@ def test_getitem_datetime_slice(self):
         s = slice("2017-01-06", "2017-01-09")
         df_equals(modin_df[s], pandas_df[s])
 
+    def test_getitem_same_name(self):
+        data = [
+            [1, 2, 3, 4],
+            [5, 6, 7, 8],
+            [9, 10, 11, 12],
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        ]
+        columns = ["c1", "c2", "c1", "c3"]
+        modin_df = pd.DataFrame(data, columns=columns)
+        pandas_df = pandas.DataFrame(data, columns=columns)
+        df_equals(modin_df["c1"], pandas_df["c1"])
+        df_equals(modin_df["c2"], pandas_df["c2"])
+        df_equals(modin_df[["c1", "c2"]], pandas_df[["c1", "c2"]])
+        df_equals(modin_df["c3"], pandas_df["c3"])
+
     @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
     def test___getattr__(self, request, data):
         modin_df = pd.DataFrame(data)