Repository: mars-project/mars

Issue:
[BUG] df.loc failed when index is an empty list
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**

df.loc failed when index is an empty list.

**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 pandas
4. Full stack of the error.
5. Minimized code to reproduce the error.

```
In [6]: import pandas as pd                                                                                                                                                                                 

In [7]: df = md.DataFrame(pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}))                                                                                                                                   

In [8]: df.loc[[]].execute()                                                                                                                                                                                
Traceback (most recent call last):
  File "/Users/qinxuye/Workspace/mars/mars/services/task/supervisor/processor.py", line 52, in inner
    return await func(processor, *args, **kwargs)
  File "/Users/qinxuye/Workspace/mars/mars/services/task/supervisor/processor.py", line 300, in get_next_stage_processor
    chunk_graph = await self._get_next_chunk_graph(self._chunk_graph_iter)
  File "/Users/qinxuye/Workspace/mars/mars/services/task/supervisor/processor.py", line 235, in _get_next_chunk_graph
    chunk_graph = await fut
  File "/Users/qinxuye/Workspace/mars/mars/lib/aio/_threads.py", line 36, in to_thread
    return await loop.run_in_executor(None, func_call)
  File "/Users/qinxuye/miniconda3/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/Users/qinxuye/Workspace/mars/mars/services/task/supervisor/processor.py", line 230, in next_chunk_graph
    return next(chunk_graph_iter)
  File "/Users/qinxuye/Workspace/mars/mars/services/task/supervisor/preprocessor.py", line 141, in tile
    for chunk_graph in chunk_graph_builder.build():
  File "/Users/qinxuye/Workspace/mars/mars/core/graph/builder/chunk.py", line 237, in build
    yield from self._build()
  File "/Users/qinxuye/Workspace/mars/mars/core/graph/builder/chunk.py", line 233, in _build
    yield from self.tiler
  File "/Users/qinxuye/Workspace/mars/mars/services/task/supervisor/preprocessor.py", line 69, in __iter__
    to_update_tileables = self._iter()
  File "/Users/qinxuye/Workspace/mars/mars/core/graph/builder/chunk.py", line 179, in _iter
    next_tileable_handlers, to_update_tileables, visited)
  File "/Users/qinxuye/Workspace/mars/mars/core/graph/builder/chunk.py", line 94, in _tile
    need_process = next(tile_handler)
  File "/Users/qinxuye/Workspace/mars/mars/core/graph/builder/chunk.py", line 70, in _tile_handler
    tiled_tileables = yield from handler.tile(tiled_tileables)
  File "/Users/qinxuye/Workspace/mars/mars/core/entity/tileables.py", line 77, in tile
    tiled_result = yield from tile_handler(op)
  File "/Users/qinxuye/Workspace/mars/mars/dataframe/indexing/loc.py", line 333, in tile
    return [(yield from handler.handle(op))]
  File "/Users/qinxuye/Workspace/mars/mars/tensor/indexing/index_lib.py", line 905, in handle
    yield from self._preprocess(context, index_infos)
  File "/Users/qinxuye/Workspace/mars/mars/tensor/indexing/index_lib.py", line 922, in _preprocess
    yield from preprocess
  File "/Users/qinxuye/Workspace/mars/mars/dataframe/indexing/index_lib.py", line 759, in preprocess
    chunk_labels = to_numpy(pd_index[abs_pos])
  File "/Users/qinxuye/miniconda3/lib/python3.7/site-packages/pandas/core/indexes/range.py", line 720, in __getitem__
    return super().__getitem__(key)
  File "/Users/qinxuye/miniconda3/lib/python3.7/site-packages/pandas/core/indexes/base.py", line 4107, in __getitem__
    result = getitem(key)
IndexError: arrays used as indices must be of integer (or boolean) type
```




Discussion:

Resolving patch:
diff --git a/mars/dataframe/indexing/index_lib.py b/mars/dataframe/indexing/index_lib.py
--- a/mars/dataframe/indexing/index_lib.py
+++ b/mars/dataframe/indexing/index_lib.py
@@ -756,7 +756,10 @@ def preprocess(self,
             for chunk_index, pos in chunk_index_to_pos.items():
                 # chunk_index and pos are all list with 1 element
                 abs_pos = pos[0] + cum_nsplit[chunk_index[0]]
-                chunk_labels = to_numpy(pd_index[abs_pos])
+                if isinstance(pd_index, pd.RangeIndex) and len(abs_pos) == 0:
+                    chunk_labels = np.array([], dtype=pd_index.dtype)
+                else:
+                    chunk_labels = to_numpy(pd_index[abs_pos])
                 chunk_index_to_labels[chunk_index[0]] = chunk_labels
 
             index_info.is_label_asc_sorted = is_asc_sorted
@@ -778,6 +781,8 @@ def process(self,
         tileable = context.tileable
         input_axis = index_info.input_axis
         chunk_index_to_labels = index_info.chunk_index_to_labels
+        full_label_size = sum(labels.size for labels
+                              in chunk_index_to_labels.values())
 
         other_index_to_iter = dict()
         chunk_index_to_info = context.chunk_index_to_info.copy()
@@ -786,8 +791,11 @@ def process(self,
             chunk_labels = chunk_index_to_labels[i]
             size = chunk_labels.size
 
-            if size == 0 and tileable.shape[0] > 0:
-                # not effected when tileable not empty and no index chosen
+            if size == 0 and full_label_size > 0 and tileable.shape[0] > 0:
+                # not effected when
+                # 1) tileable not empty
+                # 2) full index not empty
+                # 3) no index chosen for this chunk
                 del context.chunk_index_to_info[chunk_index]
                 continue
 


Test patch:
diff --git a/mars/dataframe/indexing/tests/test_indexing_execution.py b/mars/dataframe/indexing/tests/test_indexing_execution.py
--- a/mars/dataframe/indexing/tests/test_indexing_execution.py
+++ b/mars/dataframe/indexing/tests/test_indexing_execution.py
@@ -301,6 +301,17 @@ def test_loc_getitem(setup):
     expected = raw1.loc['a3', 'b']
     assert result == expected
 
+    # test empty list
+    df = df1.loc[[]]
+    result = df.execute().fetch()
+    expected = raw1.loc[[]]
+    pd.testing.assert_frame_equal(result, expected)
+
+    df = df2.loc[[]]
+    result = df.execute().fetch()
+    expected = raw2.loc[[]]
+    pd.testing.assert_frame_equal(result, expected)
+
     df = df2.loc[1:4, 'b':'d']
     result = df.execute().fetch()
     expected = raw2.loc[1:4, 'b': 'd']