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:
mrocklin: This looks good to me.  One final thing is that we might want to do is to add a regression test.  We might verify that the `.name` attribute of different calls differ.  Perhaps something like the following:

```python
assert da.argmin(x).name != da.argmin(x + 1).name
```
jakirkham: LGTM. Thanks @TAdeJong 😄

Patch:
diff --git a/dask/array/creation.py b/dask/array/creation.py
index e953c5055..1ae0e518d 100644
--- 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
index f9b004664..b4020821e 100644
--- 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)


Test patch:
diff --git a/dask/array/tests/test_reductions.py b/dask/array/tests/test_reductions.py
index 42afbbd4d..bfe54bf5f 100644
--- a/dask/array/tests/test_reductions.py
+++ b/dask/array/tests/test_reductions.py
@@ -6,7 +6,7 @@ np = pytest.importorskip('numpy')
 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