Repository: modin-project/modin
Pull request: Support boolean indexers and other properties in `loc`
* Resolves #610
* Adds a fastrack to lists of columns and boolean indexers

<!--
Thank you for your contribution!
-->

## What do these changes do?

<!-- Please give a short brief about these changes. -->

## Related issue number

<!-- Are there any issues opened that will be resolved by merging this change? -->

- [x] passes `flake8 modin`
- [x] passes `black --check modin`
- [ ] tests added and passing


Problem statement:
loc gives incorrect values when given a Boolean indexer
<!--
General questions should be asked on the mailing list modin-dev@googlegroups.com.

Before submitting an issue, please fill out the following form.
-->

### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: OSX
- **Modin installed from (source or binary)**: source
- **Modin version**: 0.6.0.dev.0
- **Python version**: 3.6
- **Exact command to reproduce**: `df.loc[df.col1 < 5]`

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->
`loc` does not support Boolean indexers. This was originally reported over email and reproduced by me locally.

### 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. -->
```python
import modin.pandas as pd
df = pd.DataFrame({"col1":[1,2,3,4,5,6], "col2":[1,2,3,4,5,6]})
df.loc[df.col1 < 5]
```

Hints from issue discussion:
Hi Devin, i check and loc does not support boolean and callable, the idea is to fallback or support it? 

Thanks @ipacheco-uy, we won't fallback to support this because it would be really expensive.

I outlined a bit of what is needed in a post on the discourse: https://discuss.modin.org/t/indexing-update-rewrite/57

Indexing needs to be updated because some of the general implementation is too slow. Indexing should be fast and correct. We also have diverged codepaths for `loc` and `__getitem__`, which doesn't make a lot of sense because they do similar things. They should at least be the same in the backend.

Commit message: Fix transposed getitem

Patch to review:
diff --git a/modin/backends/pandas/query_compiler.py b/modin/backends/pandas/query_compiler.py
index 42ce8eabf9e..9c87d36e0b9 100644
--- a/modin/backends/pandas/query_compiler.py
+++ b/modin/backends/pandas/query_compiler.py
@@ -1925,7 +1925,7 @@ def back(self, n):
     # End Head/Tail/Front/Back
 
     # __getitem__ methods
-    def getitem_column_array(self, key):
+    def getitem_column_array(self, key, numeric=False):
         """Get column data for target labels.
 
         Args:
@@ -1941,7 +1941,10 @@ def getitem_column_array(self, key):
                 .transpose()
             )
         # Convert to list for type checking
-        numeric_indices = self.columns.get_indexer_for(key)
+        if not numeric:
+            numeric_indices = self.columns.get_indexer_for(key)
+        else:
+            numeric_indices = key
         result = self.data.mask(col_indices=numeric_indices)
         # We can't just set the columns to key here because there may be
         # multiple instances of a key.
@@ -1962,7 +1965,7 @@ def getitem_row_array(self, key):
             A new QueryCompiler.
         """
         if self._is_transposed:
-            return self.transpose().getitem_column_array(key).transpose()
+            return self.transpose().getitem_column_array(key, numeric=True).transpose()
         result = self.data.mask(row_indices=key)
         # We can't just set the index to key here because there may be multiple
         # instances of a key.
diff --git a/modin/pandas/indexing.py b/modin/pandas/indexing.py
index 07eb8ff4479..e52b398bcc0 100644
--- a/modin/pandas/indexing.py
+++ b/modin/pandas/indexing.py
@@ -227,6 +227,16 @@ class _LocIndexer(_LocationIndexerBase):
     """A indexer for ray_df.loc[] functionality"""
 
     def __getitem__(self, key):
+        # When getting along a single axis,
+        if not isinstance(key, tuple):
+            # Try to fasttrack the code through already optimized path
+            try:
+                return self.df.__getitem__(key)
+            # This can happen if it is a list of rows
+            except KeyError:
+                pass
+        elif key[0] == slice(None):
+            return self.df.__getitem__(key[1])
         row_loc, col_loc, ndim, self.row_scaler, self.col_scaler = _parse_tuple(key)
         self._handle_enlargement(row_loc, col_loc)
         row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)


Code review comments:
diff_hunk: @@ -227,6 +227,16 @@ class _LocIndexer(_LocationIndexerBase):
     """A indexer for ray_df.loc[] functionality"""
 
     def __getitem__(self, key):
+        # When getting along a single axis,
+        if not isinstance(key, tuple):
+            # Try to fasttrack the code through already optimized path
+            try:
+                return self.df.__getitem__(key)
+            # This can happen if it is a list of rows
+            except KeyError:
+                pass
+        elif key[0] == slice(None):
+            return self.df.__getitem__(key[1])
path: modin/pandas/indexing.py
text: @user1:
Should we have a try-except block here as well in case it is a list of columns?

@author:
list of columns should always work here. if it's not a valid argument it will fail in `__getitem__`.

It would be good to add a check for `Series` here, because there should never be a tuple for `Series.loc`. I will add it.