Repository: modin-project/modin

Issue:
read_csv with Ray engine works incorrectly if len(names) is less than actual number of columns in csv file
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: Linux Ubuntu 18.04
- **Modin version** (`modin.__version__`): 0.7.3+350.g2ca3f34
- **Python version**: 3.8.5
- **Code we can use to reproduce**:
```
import os
os.environ["MODIN_ENGINE"] = "ray"

import pandas
import modin.pandas as pd
from modin.pandas.test.utils import df_equals

test_filename = "test.csv"
csv_single_col = """one,two
three,four
"""

try :
    with open(test_filename, "w") as f:
        f.write(csv_single_col)

    df_pandas = pandas.read_csv(test_filename, names=["col2"])
    print(df_pandas)
    df_pd = pd.read_csv(test_filename, names=["col2"])
    print(df_pd)
    df_equals(df_pd, df_pandas)
finally:
    os.remove(test_filename)
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->

### Source code / logs
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->
```
       col2
one     two
three  four
   col2
0   two
1  four
Traceback (most recent call last):
  File "test_read_csv.py", line 47, in <module>
    df_equals(df_pd, df_pandas)
  File "/localdisk/amyskov/modin2/modin/pandas/test/utils.py", line 499, in df_equals
    assert_frame_equal(
  File "/localdisk/amyskov/miniconda3_2/envs/modin_test3/lib/python3.8/site-packages/pandas/_testing.py", line 1332, in assert_frame_equal
    assert_index_equal(
  File "/localdisk/amyskov/miniconda3_2/envs/modin_test3/lib/python3.8/site-packages/pandas/_testing.py", line 679, in assert_index_equal
    _testing.assert_almost_equal(
  File "pandas/_libs/testing.pyx", line 65, in pandas._libs.testing.assert_almost_equal
  File "pandas/_libs/testing.pyx", line 174, in pandas._libs.testing.assert_almost_equal
  File "/localdisk/amyskov/miniconda3_2/envs/modin_test3/lib/python3.8/site-packages/pandas/_testing.py", line 915, in raise_assert_detail
    raise AssertionError(msg)
AssertionError: DataFrame.index are different

DataFrame.index values are different (100.0 %)
[left]:  RangeIndex(start=0, stop=2, step=1)
[right]: Index(['one', 'three'], dtype='object')
```



Discussion:

Resolving patch:
diff --git a/modin/engines/base/io/text/csv_reader.py b/modin/engines/base/io/text/csv_reader.py
--- a/modin/engines/base/io/text/csv_reader.py
+++ b/modin/engines/base/io/text/csv_reader.py
@@ -58,6 +58,7 @@ def _read(cls, filepath_or_buffer, **kwargs):
         nrows = kwargs.pop("nrows", None)
         names = kwargs.get("names", None)
         index_col = kwargs.get("index_col", None)
+        usecols = kwargs.get("usecols", None)
         if names is None:
             # For the sake of the empty df, we assume no `index_col` to get the correct
             # column names before we build the index. Because we pass `names` in, this
@@ -67,13 +68,22 @@ def _read(cls, filepath_or_buffer, **kwargs):
                 filepath_or_buffer,
                 **dict(kwargs, usecols=None, nrows=0, skipfooter=0, index_col=None),
             ).columns
+        elif index_col is None and not usecols:
+            # When names is set to some list that is smaller than the number of columns
+            # in the file, the first columns are built as a hierarchical index.
+            empty_pd_df = pandas.read_csv(filepath_or_buffer, nrows=0)
+            num_cols = len(empty_pd_df.columns)
+            if num_cols > len(names):
+                index_col = list(range(num_cols - len(names)))
+                if len(index_col) == 1:
+                    index_col = index_col[0]
+                kwargs["index_col"] = index_col
         empty_pd_df = pandas.read_csv(
             filepath_or_buffer, **dict(kwargs, nrows=0, skipfooter=0)
         )
         column_names = empty_pd_df.columns
         skipfooter = kwargs.get("skipfooter", None)
         skiprows = kwargs.pop("skiprows", None)
-        usecols = kwargs.get("usecols", None)
         usecols_md = _validate_usecols_arg(usecols)
         if usecols is not None and usecols_md[1] != "integer":
             del kwargs["usecols"]


Test patch:
diff --git a/modin/pandas/test/data/issue_2074.csv b/modin/pandas/test/data/issue_2074.csv
new file mode 100644
--- /dev/null
+++ b/modin/pandas/test/data/issue_2074.csv
@@ -0,0 +1,12 @@
+one,two, three, five, six, seven, eight
+three,three, five, six, seven, eight, nine
+one,four, three, five, six, seven, eight
+one,two, three, five, six, seven, eight
+one,two, three, five, six, seven, eight
+one,two, three, five, six, seven, eight
+three,four, five, six, seven, eight, nine
+one,two, three, five, six, seven, eight
+three,four, five, six, seven, eight, nine
+three,four, five, six, seven, eight, nine
+three,four, five, six, seven, eight, nine
+three,four, five, six, seven, eight, nine
diff --git a/modin/pandas/test/test_io.py b/modin/pandas/test/test_io.py
--- a/modin/pandas/test/test_io.py
+++ b/modin/pandas/test/test_io.py
@@ -1707,3 +1707,19 @@ def test_from_arrow():
     pandas_df = create_test_pandas_dataframe()
     modin_df = from_arrow(pa.Table.from_pandas(pandas_df))
     df_equals(modin_df, pandas_df)
+
+
+@pytest.mark.parametrize(
+    "kwargs",
+    [
+        {"names": [5, 1, 3, 4, 2, 6]},
+        {"names": [0]},
+        {"names": None, "usecols": [1, 0, 2]},
+        {"names": [3, 1, 2, 5], "usecols": [4, 1, 3, 2]},
+    ],
+)
+def test_csv_names_neq_num_cols(kwargs):
+    file_name = "modin/pandas/test/data/issue_2074.csv"
+    pandas_df = pandas.read_csv(file_name, **kwargs)
+    modin_df = pd.read_csv(file_name, **kwargs)
+    df_equals(modin_df, pandas_df)