Repository: dask/dask

Issue:
Off-by-one error on dataframe slicing
```
import dask.dataframe as dd
import pandas as pd

df = pd.DataFrame({'A': range(5), 'B': range(5)})
ddf = dd.from_pandas(df, npartitions=1)
for j in range(1, len(df) +1):
    for i in range(0, j):
        print("len(df[{}:{}]): {}".format(i, j, len(df[i:j]) == len(ddf[i:j])))
```

```
len(df[0:1]): False
len(df[0:2]): False
len(df[1:2]): False
len(df[0:3]): False
len(df[1:3]): False
len(df[2:3]): False
len(df[0:4]): False
len(df[1:4]): False
len(df[2:4]): False
len(df[3:4]): False
len(df[0:5]): True
len(df[1:5]): True
len(df[2:5]): True
len(df[3:5]): True
len(df[4:5]): True
```


Discussion:
Dask should probably raise here. DataFrame.__getitem__ is doing a iloc
here, which isn't implemented in dask.

On Mon, Jan 21, 2019 at 12:49 PM George Sakkis <notifications@github.com>
wrote:

> import dask.dataframe as dd
> import pandas as pd
>
> df = pd.DataFrame({'A': range(5), 'B': range(5)})
> ddf = dd.from_pandas(df, npartitions=1)
> for j in range(1, len(df) +1):
>     for i in range(0, j):
>         print("len(df[{}:{}]): {}".format(i, j, len(df[i:j]) == len(ddf[i:j])))
>
> len(df[0:1]): False
> len(df[0:2]): False
> len(df[1:2]): False
> len(df[0:3]): False
> len(df[1:3]): False
> len(df[2:3]): False
> len(df[0:4]): False
> len(df[1:4]): False
> len(df[2:4]): False
> len(df[3:4]): False
> len(df[0:5]): True
> len(df[1:5]): True
> len(df[2:5]): True
> len(df[3:5]): True
> len(df[4:5]): True
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/dask/dask/issues/4407>, or mute the thread
> <https://github.com/notifications/unsubscribe-auth/ABQHIg6U63swSkCCr1QOJoWuo8nk1TFcks5vFgvDgaJpZM4aLW_F>
> .
>


Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -2490,7 +2490,15 @@ def __getitem__(self, key):
             graph = HighLevelGraph.from_collections(name, dsk, dependencies=[self])
             return new_dd_object(graph, name, meta, self.divisions)
         elif isinstance(key, slice):
-            return self.loc[key]
+            from pandas.api.types import is_float_dtype
+            is_integer_slice = any(isinstance(i, Integral)
+                                   for i in (key.start, key.step, key.stop))
+            # Slicing with integer labels is always iloc based except for a
+            # float indexer for some reason
+            if is_integer_slice and not is_float_dtype(self.index.dtype):
+                self.iloc[key]
+            else:
+                return self.loc[key]
 
         if (isinstance(key, (np.ndarray, list)) or (
                 not is_dask_collection(key) and (is_series_like(key) or is_index_like(key)))):
diff --git a/dask/dataframe/indexing.py b/dask/dataframe/indexing.py
--- a/dask/dataframe/indexing.py
+++ b/dask/dataframe/indexing.py
@@ -47,15 +47,15 @@ def __getitem__(self, key):
         msg = ("'DataFrame.iloc' only supports selecting columns. "
                "It must be used like 'df.iloc[:, column_indexer]'.")
         if not isinstance(key, tuple):
-            raise ValueError(msg)
+            raise NotImplementedError(msg)
 
-        if len(key) != 2:
-            raise ValueError(msg)
+        if len(key) > 2:
+            raise ValueError("Too many indexers")
 
         iindexer, cindexer = key
 
         if iindexer != slice(None):
-            raise ValueError(msg)
+            raise NotImplementedError(msg)
 
         return self._iloc(iindexer, cindexer)
 


Test patch:
diff --git a/dask/dataframe/tests/test_indexing.py b/dask/dataframe/tests/test_indexing.py
--- a/dask/dataframe/tests/test_indexing.py
+++ b/dask/dataframe/tests/test_indexing.py
@@ -243,6 +243,22 @@ def test_getitem_slice():
     assert_eq(ddf['f':], df['f':])
 
 
+def test_getitem_integer_slice():
+    df = pd.DataFrame({'A': range(6)})
+    ddf = dd.from_pandas(df, 2)
+    # integer slicing is iloc based
+    with pytest.raises(NotImplementedError):
+        ddf[1:3]
+
+    df = pd.DataFrame({'A': range(6)},
+                      index=[1., 2., 3., 5., 10., 11.])
+    ddf = dd.from_pandas(df, 2)
+    # except for float dtype indexes
+    assert_eq(ddf[2:8], df[2:8])
+    assert_eq(ddf[2:], df[2:])
+    assert_eq(ddf[:8], df[:8])
+
+
 def test_loc_on_numpy_datetimes():
     df = pd.DataFrame({'x': [1, 2, 3]},
                       index=list(map(np.datetime64, ['2014', '2015', '2016'])))
@@ -422,10 +438,10 @@ def test_iloc_raises():
     df = pd.DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
     ddf = dd.from_pandas(df, 2)
 
-    with pytest.raises(ValueError):
+    with pytest.raises(NotImplementedError):
         ddf.iloc[[0, 1], :]
 
-    with pytest.raises(ValueError):
+    with pytest.raises(NotImplementedError):
         ddf.iloc[[0, 1], [0, 1]]
 
     with pytest.raises(ValueError):