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:

Patch:
diff --git a/dask/bag/core.py b/dask/bag/core.py
index 99f5645ec..8e174a48e 100644
--- a/dask/bag/core.py
+++ b/dask/bag/core.py
@@ -33,7 +33,7 @@ from ..base import tokenize, dont_optimize, DaskMethodsMixin
 from ..bytes import open_files
 from ..compatibility import apply, urlopen, Iterable, Iterator
 from ..context import globalmethod
-from ..core import quote, istask, get_dependencies, reverse_dict
+from ..core import quote, istask, get_dependencies, reverse_dict, flatten
 from ..delayed import Delayed, unpack_collections
 from ..highlevelgraph import HighLevelGraph
 from ..multiprocessing import get as mpget
@@ -85,7 +85,7 @@ def lazify(dsk):
     return valmap(lazify_task, dsk)
 
 
-def inline_singleton_lists(dsk, dependencies=None):
+def inline_singleton_lists(dsk, keys, dependencies=None):
     """ Inline lists that are only used once.
 
     >>> d = {'b': (list, 'a'),
@@ -100,10 +100,11 @@ def inline_singleton_lists(dsk, dependencies=None):
                         for k, v in dsk.items()}
     dependents = reverse_dict(dependencies)
 
-    keys = [k for k, v in dsk.items()
-            if istask(v) and v and v[0] is list and len(dependents[k]) == 1]
-    dsk = inline(dsk, keys, inline_constants=False)
-    for k in keys:
+    inline_keys = {k for k, v in dsk.items()
+                   if istask(v) and v and v[0] is list and len(dependents[k]) == 1}
+    inline_keys.difference_update(flatten(keys))
+    dsk = inline(dsk, inline_keys, inline_constants=False)
+    for k in inline_keys:
         del dsk[k]
     return dsk
 
@@ -114,7 +115,7 @@ def optimize(dsk, keys, fuse_keys=None, rename_fused_keys=True, **kwargs):
     dsk2, dependencies = cull(dsk, keys)
     dsk3, dependencies = fuse(dsk2, keys + (fuse_keys or []), dependencies,
                               rename_keys=rename_fused_keys)
-    dsk4 = inline_singleton_lists(dsk3, dependencies)
+    dsk4 = inline_singleton_lists(dsk3, keys, dependencies)
     dsk5 = lazify(dsk4)
     return dsk5
 
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
index 3b9553ee0..0245a4ca7 100644
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -2490,7 +2490,15 @@ class DataFrame(_Frame):
             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
index 853ca9611..a1e190fd7 100644
--- a/dask/dataframe/indexing.py
+++ b/dask/dataframe/indexing.py
@@ -47,15 +47,15 @@ class _iLocIndexer(_IndexerBase):
         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/bag/tests/test_bag.py b/dask/bag/tests/test_bag.py
index 01a0e3feb..01120544a 100644
--- a/dask/bag/tests/test_bag.py
+++ b/dask/bag/tests/test_bag.py
@@ -502,18 +502,24 @@ def test_inline_singleton_lists():
     inp = {'b': (list, 'a'),
            'c': (f, 'b', 1)}
     out = {'c': (f, (list, 'a'), 1)}
-    assert inline_singleton_lists(inp) == out
+    assert inline_singleton_lists(inp, ['c']) == out
 
     out = {'c': (f, 'a', 1)}
     assert optimize(inp, ['c'], rename_fused_keys=False) == out
 
+    # If list is an output key, don't fuse it
+    assert inline_singleton_lists(inp, ['b', 'c']) == inp
+    assert optimize(inp, ['b', 'c'], rename_fused_keys=False) == inp
+
     inp = {'b': (list, 'a'),
            'c': (f, 'b', 1),
            'd': (f, 'b', 2)}
-    assert inline_singleton_lists(inp) == inp
+    assert inline_singleton_lists(inp, ['c', 'd']) == inp
 
-    inp = {'b': (4, 5)} # doesn't inline constants
-    assert inline_singleton_lists(inp) == inp
+    # Doesn't inline constants
+    inp = {'b': (4, 5),
+           'c': (f, 'b')}
+    assert inline_singleton_lists(inp, ['c']) == inp
 
 
 def test_take():
diff --git a/dask/dataframe/tests/test_indexing.py b/dask/dataframe/tests/test_indexing.py
index b37aac27e..ce3668e03 100644
--- 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):