Repository: dask/dask

Issue:
Stacking list of arg reduction's produces duplicates of last list element
This is weird. As I do often, I create a list of _n_ dask.arrays in a for-loop for a number of cases, and stack this together to one big dask array. However, if I stack together `da.argmin` operations, the array contains _n_ copies of the last argmin in the list. This does not happen for regular `da.min` or `da.sum`, but is does happen for `da.argmax`, so I suspect a issue in the general arg_reduction.

A reproduction example:

```python
import dask.array as da
import numpy as np
import matplotlib.pyplot as plt

Data = da.random.random((3,3,20,3), chunks=(3,1,-1,1))
minima = []
argminima = []
for index in range(Data.shape[-1]):
    minima.append(da.min(Data[...,index], axis=2))
    argminima.append(da.argmin(Data[...,index], axis=2))
    
fig,ax = plt.subplots(Data.shape[-1], 4, figsize=[9,6])
for index in range(Data.shape[-1]):
    ax[index,0].imshow(minima[index].compute())
    ax[index,1].imshow(argminima[index].compute())
minimb = da.stack(minima)
argminimb = da.stack(argminima)
for index in range(Data.shape[-1]):
    ax[index,2].imshow(minimb[index,:,:].compute())
    ax[index,3].imshow(argminimb[index,:,:].compute())
for i, text in enumerate(["min_before", "argmin_before", "min_after", "argmin_after"]):
    ax[0,i].set_title(text)
plt.show()
```
![image](https://user-images.githubusercontent.com/6816964/45033289-5c796080-b054-11e8-8362-53b6fa583cda.png)

Note how for `da.min` the data before and after stacking is the same, but different for each iteration, while for `da.argmin` after stacking all slices are the same.




Discussion:
Okay, one step further in debugging this: all the argminima elements have exactly the same `name` attribute, while the minima have different tokens.
> Okay, one step further in debugging this: all the argminima elements have exactly the same name attribute, while the minima have different tokens.

That's definitely an issue.  It looks like the problem is here:

https://github.com/dask/dask/blob/09100d02ad8f2b23da70234707888b1374dd46bd/dask/array/reductions.py#L590-L617

Note that when we form the name we only include `chunk` and `axis`, but we should really be including all of the input parameters to make sure that if we change any of them (like the input array) that the output array's names will differ.

Are you comfortable making this change @TAdeJong ?

Resolving patch:
diff --git a/dask/array/creation.py b/dask/array/creation.py
--- a/dask/array/creation.py
+++ b/dask/array/creation.py
@@ -943,7 +943,7 @@ def pad_udf(array, pad_width, mode, **kwargs):
 
         result = result.map_blocks(
             wrapped_pad_func,
-            token="pad",
+            name="pad",
             dtype=result.dtype,
             pad_func=mode,
             iaxis_pad_width=pad_width[d],
diff --git a/dask/array/reductions.py b/dask/array/reductions.py
--- a/dask/array/reductions.py
+++ b/dask/array/reductions.py
@@ -614,7 +614,8 @@ def arg_reduction(x, chunk, combine, agg, axis=None, split_every=None, out=None)
                         "got '{0}'".format(axis))
 
     # Map chunk across all blocks
-    name = 'arg-reduce-chunk-{0}'.format(tokenize(chunk, axis))
+    name = 'arg-reduce-{0}'.format(tokenize(axis, x, chunk,
+                                            combine, split_every))
     old = x.name
     keys = list(product(*map(range, x.numblocks)))
     offsets = list(product(*(accumulate(operator.add, bd[:-1], 0)
@@ -714,7 +715,8 @@ def cumreduction(func, binop, ident, x, axis=None, dtype=None, out=None):
 
     m = x.map_blocks(func, axis=axis, dtype=dtype)
 
-    name = '%s-axis=%d-%s' % (func.__name__, axis, tokenize(x, dtype))
+    name = '{0}-{1}'.format(func.__name__, tokenize(func, axis, binop,
+                                                    ident, x, dtype))
     n = x.numblocks[axis]
     full = slice(None, None, None)
     slc = (full,) * axis + (slice(-1, None),) + (full,) * (x.ndim - axis - 1)
diff --git a/dask/array/slicing.py b/dask/array/slicing.py
--- a/dask/array/slicing.py
+++ b/dask/array/slicing.py
@@ -69,7 +69,7 @@ def sanitize_index(ind):
         return np.asanyarray(nonzero)
     elif np.issubdtype(index_array.dtype, np.integer):
         return index_array
-    elif np.issubdtype(index_array.dtype, float):
+    elif np.issubdtype(index_array.dtype, np.floating):
         int_index = index_array.astype(np.intp)
         if np.allclose(index_array, int_index):
             return int_index


Test patch:
diff --git a/dask/array/tests/test_reductions.py b/dask/array/tests/test_reductions.py
--- a/dask/array/tests/test_reductions.py
+++ b/dask/array/tests/test_reductions.py
@@ -6,7 +6,7 @@
 import dask.array as da
 from dask.array.utils import assert_eq as _assert_eq, same_keys
 from dask.core import get_deps
-from dask.context import set_options
+import dask.config as config
 
 
 def assert_eq(a, b):
@@ -139,7 +139,7 @@ def test_arg_reductions(dfunc, func):
     assert_eq(dfunc(a, 0), func(x, 0))
     assert_eq(dfunc(a, 1), func(x, 1))
     assert_eq(dfunc(a, 2), func(x, 2))
-    with set_options(split_every=2):
+    with config.set(split_every=2):
         assert_eq(dfunc(a), func(x))
         assert_eq(dfunc(a, 0), func(x, 0))
         assert_eq(dfunc(a, 1), func(x, 1))
@@ -368,7 +368,7 @@ def test_tree_reduce_depth():
 
 def test_tree_reduce_set_options():
     x = da.from_array(np.arange(242).reshape((11, 22)), chunks=(3, 4))
-    with set_options(split_every={0: 2, 1: 3}):
+    with config.set(split_every={0: 2, 1: 3}):
         assert_max_deps(x.sum(), 2 * 3)
         assert_max_deps(x.sum(axis=0), 2)
 
@@ -487,3 +487,14 @@ def test_topk_argtopk3():
               da.topk(a, 5, axis=1, split_every=2))
     assert_eq(a.argtopk(5, axis=1, split_every=2),
               da.argtopk(a, 5, axis=1, split_every=2))
+
+
+@pytest.mark.parametrize('func', [da.cumsum, da.cumprod,
+                                  da.argmin, da.argmax,
+                                  da.min, da.max,
+                                  da.nansum, da.nanmax])
+def test_regres_3940(func):
+    a = da.ones((5,2), chunks=(2,2))
+    assert func(a).name != func(a + 1).name
+    assert func(a, axis=0).name != func(a).name
+    assert func(a, axis=0).name != func(a, axis=1).name