Repository: mars-project/mars

Issue:
[BUG] `train_test_split` failed with error: ValueError: DataFrame constructor not properly called!
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**

`train_test_split` failed with error: ValueError: DataFrame constructor not properly called!

**To Reproduce**
To help us reproducing this bug, please provide information below:
1. Your Python version
2. The version of Mars you use
3. Versions of crucial packages, such as numpy, scipy and protobuf
4. Full stack of the error.
5. Minimized code to reproduce the error.

```
In [1]: import mars.dataframe as md 

In [8]: X = df[['userId', 'rating']]                                            

In [9]: y = df['movieId']                                                       

In [11]: train_test_split(X, y, train_size=0.7, random_state=0)                 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-11-94d1aec833af> in <module>
----> 1 train_test_split(X, y, train_size=0.7, random_state=0)

~/Workspace/mars/mars/learn/model_selection/_split.py in train_test_split(*arrays, **options)
    112         raise TypeError(f"Invalid parameters passed: {options}")
    113 
--> 114     arrays = indexable(*arrays, session=session, run_kwargs=run_kwargs)
    115 
    116     n_samples = _num_samples(arrays[0])

~/Workspace/mars/mars/learn/utils/validation.py in indexable(session, run_kwargs, *iterables)
    139         List of objects to ensure sliceability.
    140     """
--> 141     result = [_make_indexable(X) for X in iterables]
    142     check_consistent_length(*result, session=session,
    143                             run_kwargs=run_kwargs)

~/Workspace/mars/mars/learn/utils/validation.py in <listcomp>(.0)
    139         List of objects to ensure sliceability.
    140     """
--> 141     result = [_make_indexable(X) for X in iterables]
    142     check_consistent_length(*result, session=session,
    143                             run_kwargs=run_kwargs)

~/Workspace/mars/mars/learn/utils/validation.py in _make_indexable(iterable)
    119         return mt.tensor(iterable)
    120     elif hasattr(iterable, "iloc"):
--> 121         return md.DataFrame(iterable)
    122     elif hasattr(iterable, "__getitem__"):
    123         return mt.tensor(iterable)

~/Workspace/mars/mars/dataframe/initializer.py in __init__(self, data, index, columns, dtype, copy, chunk_size, gpu, sparse)
     55                                            columns=columns, gpu=gpu, sparse=sparse)
     56         else:
---> 57             pdf = pd.DataFrame(data, index=index, columns=columns, dtype=dtype, copy=copy)
     58             df = from_pandas_df(pdf, chunk_size=chunk_size, gpu=gpu, sparse=sparse)
     59         super().__init__(df.data)

~/miniconda3/lib/python3.7/site-packages/pandas/core/frame.py in __init__(self, data, index, columns, dtype, copy)
    527         else:
    528             if index is None or columns is None:
--> 529                 raise ValueError("DataFrame constructor not properly called!")
    530 
    531             if not dtype:

ValueError: DataFrame constructor not properly called!
```



Discussion:

Resolving patch:
diff --git a/mars/deploy/kubernetes/config.py b/mars/deploy/kubernetes/config.py
--- a/mars/deploy/kubernetes/config.py
+++ b/mars/deploy/kubernetes/config.py
@@ -305,6 +305,9 @@ def __init__(self, name, image, replicas, resource_request=None, resource_limit=
     def add_env(self, name, value=None, field_path=None):
         self._envs[name] = ContainerEnvConfig(name, value=value, field_path=field_path)
 
+    def remove_env(self, name):  # pragma: no cover
+        self._envs.pop(name, None)
+
     def add_simple_envs(self, envs):
         for k, v in envs.items() or ():
             self.add_env(k, v)
diff --git a/mars/learn/utils/validation.py b/mars/learn/utils/validation.py
--- a/mars/learn/utils/validation.py
+++ b/mars/learn/utils/validation.py
@@ -118,7 +118,10 @@ def _make_indexable(iterable):
     if issparse(iterable):
         return mt.tensor(iterable)
     elif hasattr(iterable, "iloc"):
-        return md.DataFrame(iterable)
+        if iterable.ndim == 1:
+            return md.Series(iterable)
+        else:
+            return md.DataFrame(iterable)
     elif hasattr(iterable, "__getitem__"):
         return mt.tensor(iterable)
     elif iterable is None:


Test patch:
diff --git a/mars/learn/model_selection/tests/test_split.py b/mars/learn/model_selection/tests/test_split.py
--- a/mars/learn/model_selection/tests/test_split.py
+++ b/mars/learn/model_selection/tests/test_split.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import itertools
 import unittest
 
 import numpy as np
@@ -159,3 +160,23 @@ def testTrainTestplitListInput(self):
             np.testing.assert_equal(y_train2, y_train3)
             np.testing.assert_equal(X_test1, X_test3)
             np.testing.assert_equal(y_test3, y_test2)
+
+    def testMixiedInputTypeTrainTestSplit(self):
+        rs = np.random.RandomState(0)
+        df_raw = pd.DataFrame(rs.rand(10, 4))
+        df = md.DataFrame(df_raw, chunk_size=5)
+        X, y = df.iloc[:, :-1], df.iloc[:, -1]
+
+        for x_to_tensor, y_to_tensor in itertools.product(range(1), range(1)):
+            x = X
+            if x_to_tensor:
+                x = mt.tensor(x)
+            yy = y
+            if y_to_tensor:
+                yy = mt.tensor(yy)
+
+            x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=0)
+            self.assertIsInstance(x_train, type(x))
+            self.assertIsInstance(x_test, type(x))
+            self.assertIsInstance(y_train, type(yy))
+            self.assertIsInstance(y_test, type(yy))