internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: dask/dask

Issue:
sum(axis=-1)
Calling x.sum(axis=-1) where x is a 3D dask array produces the wrong result. Details in [this notebook](http://nbviewer.ipython.org/gist/alimanfoo/0c45c1d2d8c09c02bd2f/dask.ipynb). 



Discussion:

Resolving patch:
diff --git a/dask/array/reductions.py b/dask/array/reductions.py
--- a/dask/array/reductions.py
+++ b/dask/array/reductions.py
@@ -20,6 +20,7 @@ def reduction(x, chunk, aggregate, axis=None, keepdims=None, dtype=None):
         axis = tuple(range(x.ndim))
     if isinstance(axis, int):
         axis = (axis,)
+    axis = tuple(i if i >= 0 else x.ndim + i for i in axis)
 
     chunk2 = partial(chunk, axis=axis, keepdims=True)
     aggregate2 = partial(aggregate, axis=axis, keepdims=keepdims)
@@ -289,6 +290,9 @@ def arg_reduction(a, func, argfunc, axis=0, dtype=None):
                 "  Before:  x.argmin()\n"
                 "  After:   x.argmin(axis=0)\n")
 
+    if axis < 0:
+        axis = a.ndim + axis
+
     def argreduce(x):
         """ Get both min/max and argmin/argmax of each block """
         return (func(x, axis=axis), argfunc(x, axis=axis))


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
@@ -33,9 +33,17 @@ def test_reductions():
 
     assert eq(a.argmin(axis=1), x.argmin(axis=1))
     assert eq(a.argmax(axis=0), x.argmax(axis=0))
-    # assert eq(a.argmin(), x.argmin())
 
 
+def test_reductions_with_negative_axes():
+    x = np.random.random((4, 4, 4))
+    a = da.from_array(x, chunks=2)
+
+    assert eq(a.argmin(axis=-1), x.argmin(axis=-1))
+
+    assert eq(a.sum(axis=-1), x.sum(axis=-1))
+    assert eq(a.sum(axis=(0, -1)), x.sum(axis=(0, -1)))
+
 def test_nan():
     x = np.array([[1, np.nan, 3, 4],
                   [5, 6, 7, np.nan],
Repository: dask/dask

Issue:
Should rewrite rules traverse terms?
I end up needing something like the following for the column-store optimizations in #325 

``` python
In [1]: from dask.rewrite import RewriteRule, RuleSet

In [2]: rs = RuleSet(RewriteRule((1, 2), 3, ()))

In [3]: rs.rewrite((1, 2))
Out[3]: 3

In [4]: rs.rewrite((4, (1, 2)))  # I wish that this was (4, 3)
Out[4]: (4, (1, 2))
```

Should rulesets be willing to apply rewrite rules within terms and within lists?  Is this easy to accomplish with the existing system?

cc @jcrist 



Discussion:
Right now we only traverse tasks and lists. `(1, 2)` isn't a task (`1` isn't a callable), and the tuple isn't a list. We could go into tuples the same way we do lists though, the [spec](http://dask.pydata.org/en/latest/spec.html) didn't seem to indicate that this was allowed, so I didn't implement it. Easy fix, I can take care of this.

Hrm, good point.   Let me make sure that this is useful.

I have tasks like the following:

``` python
 ('f-2', 221): (<function apply>,
  <function dask.dataframe.core.<lambda>>,
  (list,
   [(<function operator.getitem>,
     (<unbound method Castra.load_partition>,
      <castra.core.Castra at 0x7f694d413990>,
      '2014-12-11T08:00:54.000000000-0800--2014-12-12T08:59:59.000000000-0800',
      ['event', 'ip', 'software_branch', 'arch', 'platform', 'version']),
     'software_branch')])),
```

and a rewrite rule that looks like this

``` python
        RewriteRule((getitem, (Castra.load_partition, '~c', '~part', '~cols1'),
                              '~cols2'),
                    (Castra.load_partition, '~c', '~part', '~cols2'),
                    ('~c', '~part', '~cols1', '~cols2')))
```

but the rule doesn't seem to trigger

Hmmm, that looks like a bug. I'll take a look tomorrow.


Resolving patch:
diff --git a/dask/rewrite.py b/dask/rewrite.py
--- a/dask/rewrite.py
+++ b/dask/rewrite.py
@@ -358,12 +358,11 @@ def _top_level(net, term):
 
 
 def _bottom_up(net, term):
-    if not istask(term):
-        return net._rewrite(term)
-    else:
-        new_args = tuple(_bottom_up(net, t) for t in args(term))
-        new_term = (head(term),) + new_args
-    return net._rewrite(new_term)
+    if istask(term):
+        term = (head(term),) + tuple(_bottom_up(net, t) for t in args(term))
+    elif isinstance(term, list):
+        term = [_bottom_up(net, t) for t in args(term)]
+    return net._rewrite(term)
 
 
 strategies = {'top_level': _top_level,


Test patch:
diff --git a/dask/tests/test_rewrite.py b/dask/tests/test_rewrite.py
--- a/dask/tests/test_rewrite.py
+++ b/dask/tests/test_rewrite.py
@@ -128,9 +128,10 @@ def test_matches():
 
 
 def test_rewrite():
+    # Rewrite inside list
     term = (sum, [(add, 1, 1), (add, 1, 1), (add, 1, 1)])
     new_term = rs.rewrite(term)
-    assert new_term == (add, (add, (add, 1, 1), (add, 1, 1)), (add, 1, 1))
+    assert new_term == (add, (add, (inc, 1), (inc, 1)), (inc, 1))
     # Rules aren't applied to exhaustion, this can be further simplified
     new_term = rs.rewrite(new_term)
     assert new_term == (add, (add, (double, 1), 2), (inc, 1))
Repository: dask/dask

Issue:
dask.array.rechunk no longer works with an integer
This worked on dask 0.5, but now raises on master:

```
In [5]: da.from_array(np.arange(5), chunks=4).rechunk(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-1383bbf6e4cd> in <module>()
----> 1 da.from_array(np.arange(5), chunks=4).rechunk(3)

/Users/shoyer/dev/dask/dask/array/core.pyc in rechunk(self, chunks)
   1069     def rechunk(self, chunks):
   1070         from .rechunk import rechunk
-> 1071         return rechunk(self, chunks)
   1072
   1073

/Users/shoyer/dev/dask/dask/array/rechunk.pyc in rechunk(x, chunks)
    198             chunks = blockdims_dict_to_tuple(x.chunks, chunks)
    199     chunks = tuple(lc if lc is not None else rc
--> 200                    for lc, rc in zip(chunks, x.chunks))
    201     chunks = normalize_chunks(chunks, x.shape)
    202     if not len(chunks) == x.ndim or tuple(map(sum, chunks)) != x.shape:

TypeError: zip argument #1 must support iteration
```

This causes a test failure for xray. On a related note, I'm now testing dask master on Travis with xray as (the last) optional test: https://travis-ci.org/xray/xray



Discussion:
On it.


Resolving patch:
diff --git a/dask/array/rechunk.py b/dask/array/rechunk.py
--- a/dask/array/rechunk.py
+++ b/dask/array/rechunk.py
@@ -196,6 +196,8 @@ def rechunk(x, chunks):
             chunks = blockshape_dict_to_tuple(x.chunks, chunks)
         else:
             chunks = blockdims_dict_to_tuple(x.chunks, chunks)
+    if not isinstance(chunks, (tuple, list)):
+        chunks = (chunks,)
     chunks = tuple(lc if lc is not None else rc
                    for lc, rc in zip(chunks, x.chunks))
     chunks = normalize_chunks(chunks, x.shape)


Test patch:
diff --git a/dask/array/tests/test_rechunk.py b/dask/array/tests/test_rechunk.py
--- a/dask/array/tests/test_rechunk.py
+++ b/dask/array/tests/test_rechunk.py
@@ -170,3 +170,10 @@ def test_rechunk_with_null_dimensions():
     x = da.from_array(np.ones((24, 24)), chunks=(4, 8))
     assert (x.rechunk(chunks=(None, 4)).chunks ==
             da.ones((24, 24), chunks=(4, 4)).chunks)
+
+
+def test_rechunk_with_integer():
+    x = da.from_array(np.arange(5), chunks=4)
+    y = x.rechunk(3)
+    assert y.chunks == ((3, 2),)
+    assert (x.compute() == y.compute()).all()
Repository: dask/dask

Issue:
Write optimization docs
This would include core optimizations in dask.optimize, collection specific optimizations, and rewrite rules.



Discussion:

Resolving patch:
diff --git a/dask/optimize.py b/dask/optimize.py
--- a/dask/optimize.py
+++ b/dask/optimize.py
@@ -14,7 +14,6 @@ def cull(dsk, keys):
 
     Examples
     --------
-
     >>> d = {'x': 1, 'y': (inc, 'x'), 'out': (add, 'x', 10)}
     >>> cull(d, 'out')  # doctest: +SKIP
     {'x': 1, 'out': (add, 'x', 10)}
@@ -43,7 +42,6 @@ def fuse(dsk, keys=None):
 
     Examples
     --------
-
     >>> d = {'a': 1, 'b': (inc, 'a'), 'c': (inc, 'b')}
     >>> fuse(d)  # doctest: +SKIP
     {'c': (inc, (inc, 1))}
@@ -116,7 +114,6 @@ def inline(dsk, keys=None, inline_constants=True):
 
     Examples
     --------
-
     >>> d = {'x': 1, 'y': (inc, 'x'), 'z': (add, 'x', 'y')}
     >>> inline(d)  # doctest: +SKIP
     {'y': (inc, 1), 'z': (add, 1, 'y')}
@@ -164,6 +161,8 @@ def inline(dsk, keys=None, inline_constants=True):
 def inline_functions(dsk, fast_functions=None, inline_constants=False):
     """ Inline cheap functions into larger operations
 
+    Examples
+    --------
     >>> dsk = {'out': (add, 'i', 'd'),  # doctest: +SKIP
     ...        'i': (inc, 'x'),
     ...        'd': (double, 'y'),
@@ -193,6 +192,8 @@ def inline_functions(dsk, fast_functions=None, inline_constants=False):
 def functions_of(task):
     """ Set of functions contained within nested task
 
+    Examples
+    --------
     >>> task = (add, (mul, 1, 2), (inc, 3))  # doctest: +SKIP
     >>> functions_of(task)  # doctest: +SKIP
     set([add, mul, inc])
@@ -225,7 +226,6 @@ def dealias(dsk):
 
     Examples
     --------
-
     >>> dsk = {'a': (range, 5),
     ...        'b': 'a',
     ...        'c': 'b',
@@ -413,7 +413,7 @@ def merge_sync(dsk1, dsk2):
         A mapping between the keys from `dsk2` to their new names in `new_dsk`.
 
     Examples
-    -------
+    --------
     >>> from operator import add, mul
     >>> dsk1 = {'a': 1, 'b': (add, 'a', 10), 'c': (mul, 'b', 5)}
     >>> dsk2 = {'x': 1, 'y': (add, 'x', 10), 'z': (mul, 'y', 2)}


Test patch:
Repository: dask/dask

Issue:
BUG / DOC: DataFrame.value_counts doesn't sort the result, docstring says it does
This is dask `0.6.0`.

``` python
In [8]: import dask.dataframe as dd

In [9]: df = dd.read_csv('tmp.csv')

In [10]: df.head()
Out[10]:
   A  B
0  a  1
1  b  2
2  a  1
3  a  1
4  b  2
5  b  2
6  b  2
7  c  3
8  b  2
9  c  3

In [11]: df.A.value_counts().compute()
Out[11]:
a    3
b    5
c    2
dtype: int64
```

I was surprised when it _did_ say the results would be sorted (by default).

```
Returns object containing counts of unique values.

The resulting object will be in descending order so that the
first element is the most frequently-occurring element.
Excludes NA values by default.

Parameters
----------
normalize : boolean, default False
    If True then the object returned will contain the relative
    frequencies of the unique values.
sort : boolean, default True
    Sort by values
ascending : boolean, default False
    Sort in ascending order
```

Not sure if this is a problem with the docstring or the implementation. A couple other things that may help track this down. It seems to be sorting the index (same thing on a larger example), and it raises a `TypeError` when you pass in sort.

``` python
In [23]: df.A.value_counts(sort=True).compute()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-23-9f674ed286e0> in <module>()
----> 1 df.A.value_counts(sort=True).compute()

TypeError: value_counts() got an unexpected keyword argument 'sort'
```



Discussion:

Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -490,7 +490,7 @@ def std(self, ddof=1):
     @wraps(pd.Series.value_counts)
     def value_counts(self):
         chunk = lambda s: s.value_counts()
-        agg = lambda s: s.groupby(level=0).sum()
+        agg = lambda s: s.groupby(level=0).sum().sort(inplace=False, ascending=False)
         return aca(self, chunk=chunk, aggregate=agg, columns=self.columns)
 
     @wraps(pd.Series.nlargest)


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -283,9 +283,11 @@ def test_cache():
 
 
 def test_value_counts():
-    result = d.b.value_counts().compute()
-    expected = full.b.value_counts()
-    assert eq(result.sort_index(), expected.sort_index())
+    df = pd.DataFrame({'x': [1, 2, 1, 3, 3, 1, 4]})
+    a = dd.from_pandas(df, npartitions=3)
+    result = a.x.value_counts()
+    expected = df.x.value_counts()
+    assert eq(result, expected)
 
 
 def test_isin():
Repository: dask/dask

Issue:
Invalid magics in dask.imperitive.Value
``` python
__inv__
__concat__
__not__
```

You are setting these to `operator.inv`, `operator.concat` and `operator.not_`; however, `inv` is an alias for `invert` and the magic is already defined. `concat` is an alias for `add` that is more readable for lists and collections. Also, `not_` cannot be overridden with a function, `__not__` is not called.

If you are using these names internally then I apologize; however, I would urge you not to "invent" your own magic methods:

from pep8:

> `__double_leading_and_trailing_underscore__` : "magic" objects or attributes that live in user-controlled namespaces. E.g. `__init__`, `__import__` or `__file__` . Never invent such names; only use them as documented. 



Discussion:

Resolving patch:
diff --git a/dask/imperative.py b/dask/imperative.py
--- a/dask/imperative.py
+++ b/dask/imperative.py
@@ -257,8 +257,6 @@ def __bool__(self):
     __abs__ = do(operator.abs, True)
     __add__ = do(operator.add, True)
     __and__ = do(operator.and_, True)
-    __concat__ = do(operator.concat, True)
-    __delitem__ = do(operator.delitem, True)
     __div__ = do(operator.floordiv, True)
     __eq__ = do(operator.eq, True)
     __floordiv__ = do(operator.floordiv, True)
@@ -266,7 +264,6 @@ def __bool__(self):
     __getitem__ = do(operator.getitem, True)
     __gt__ = do(operator.gt, True)
     __index__ = do(operator.index, True)
-    __inv__ = do(operator.inv, True)
     __invert__ = do(operator.invert, True)
     __le__ = do(operator.le, True)
     __lshift__ = do(operator.lshift, True)
@@ -275,7 +272,6 @@ def __bool__(self):
     __mul__ = do(operator.mul, True)
     __ne__ = do(operator.ne, True)
     __neg__ = do(operator.neg, True)
-    __not__ = do(operator.not_, True)
     __or__ = do(operator.or_, True)
     __pos__ = do(operator.pos, True)
     __pow__ = do(operator.pow, True)


Test patch:
Repository: dask/dask

Issue:
dask.array indexing with Ellipsis (...) broken
```
>>> da.ones(4, chunks=2)[...].compute()
KeyError: ('x_8', 1)
```



Discussion:

Resolving patch:
diff --git a/dask/array/slicing.py b/dask/array/slicing.py
--- a/dask/array/slicing.py
+++ b/dask/array/slicing.py
@@ -98,7 +98,10 @@ def slice_array(out_name, in_name, blockdims, index):
 
     # x[:, :, :] - Punt and return old value
     if all(index == slice(None, None, None) for index in index):
-        return {out_name: in_name}, blockdims
+        suffixes = product(*[range(len(bd)) for bd in blockdims])
+        dsk = dict(((out_name,) + s, (in_name,) + s)
+                   for s in suffixes)
+        return dsk, blockdims
 
     # Add in missing colons at the end as needed.  x[5] -> x[5, :, :]
     missing = len(blockdims) - (len(index) - index.count(None))


Test patch:
diff --git a/dask/array/tests/test_array_core.py b/dask/array/tests/test_array_core.py
--- a/dask/array/tests/test_array_core.py
+++ b/dask/array/tests/test_array_core.py
@@ -1216,3 +1216,7 @@ def test_h5py_newaxis():
             assert d[None, :, :].compute(get=get_sync).shape == (1, 10, 10)
             assert d[:, None, :].compute(get=get_sync).shape == (10, 1, 10)
             assert d[:, :, None].compute(get=get_sync).shape == (10, 10, 1)
+
+
+def test_ellipsis_slicing():
+    assert eq(da.ones(4, chunks=2)[...], np.ones(4))
diff --git a/dask/array/tests/test_slicing.py b/dask/array/tests/test_slicing.py
--- a/dask/array/tests/test_slicing.py
+++ b/dask/array/tests/test_slicing.py
@@ -217,12 +217,14 @@ def test_slice_array_2d():
 
 def test_slice_optimizations():
     #bar[:]
-    expected = {'foo':'bar'}
+    expected = {('foo', 0): ('bar', 0)}
     result, chunks = slice_array('foo', 'bar', [[100]], (slice(None,None,None),))
     assert expected == result
 
     #bar[:,:,:]
-    expected = {'foo':'bar'}
+    expected = {('foo', 0) : ('bar', 0),
+                ('foo', 1) : ('bar', 1),
+                ('foo', 2) : ('bar', 2)}
     result, chunks = slice_array('foo', 'bar', [(100,1000,10000)],
                         (slice(None,None,None),slice(None,None,None),
                          slice(None,None,None)))
Repository: dask/dask

Issue:
df.query() method for dask.dataframe
[Method](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.query.html)

Thanks!



Discussion:

Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -644,6 +644,30 @@ def assign(self, **kwargs):
 
         return elemwise(_assign, self, *pairs, columns=list(df2.columns))
 
+    def query(self, expr, **kwargs):
+        """ Blocked version of pd.DataFrame.query
+
+        This is like the sequential version except that this will also happen
+        in many threads.  This may conflict with ``numexpr`` which will use
+        multiple threads itself.  We recommend that you set numexpr to use a
+        single thread
+
+            import numexpr
+            numexpr.set_nthreads(1)
+
+        The original docstring follows below:\n
+        """ + pd.DataFrame.query.__doc__
+        name = 'query' + next(tokens)
+        if kwargs:
+            dsk = dict(((name, i), (apply, pd.DataFrame.query,
+                                    ((self._name, i), (expr,), kwargs)))
+                       for i in range(self.npartitions))
+        else:
+            dsk = dict(((name, i), (pd.DataFrame.query, (self._name, i), expr))
+                       for i in range(self.npartitions))
+
+        return DataFrame(merge(dsk, self.dask), name, self.columns, self.divisions)
+
     def to_castra(self, fn=None, categories=None):
         """ Write DataFrame to Castra on-disk store
 


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -600,3 +600,9 @@ def test_categorical_set_index():
         b = a.set_index(a.y)
         df2 = df.set_index(df.y)
         assert list(b.index.compute()), list(df2.index)
+
+
+def test_query():
+    df = pd.DataFrame({'x': [1, 2, 3, 4], 'y': [5, 6, 7, 8]})
+    a = dd.from_pandas(df, npartitions=2)
+    assert eq(a.query('x**2 > y'), df.query('x**2 > y'))
Repository: dask/dask

Issue:
Another dask.array bug with scalar arrays
```
import dask.array as da
import numpy as np

x = da.from_array(np.array(1.0), chunks=())
(x == x).all().compute()
# IndexError: tuple index out of range

# or alternatively
(x + x).sum().compute()
```

Arguably, the source of this bug is the fact that numpy turns 0d arrays into scalars after binary operations:

```
In [8]: a = np.array(1.0)

In [9]: a + a
Out[9]: 2.0

In [10]: type(a + a)
Out[10]: numpy.float64
```

Unfortunately, dask still needs to work around this...



Discussion:

Resolving patch:
diff --git a/dask/array/core.py b/dask/array/core.py
--- a/dask/array/core.py
+++ b/dask/array/core.py
@@ -379,6 +379,8 @@ def _concatenate2(arrays, axes=[]):
     """
     if isinstance(arrays, Iterator):
         arrays = list(arrays)
+    if not isinstance(arrays, (list, tuple)):
+        return arrays
     if len(axes) > 1:
         arrays = [_concatenate2(a, axes=axes[1:]) for a in arrays]
     return np.concatenate(arrays, axis=axes[0])


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
@@ -227,3 +227,8 @@ def test_0d_array():
     x = da.sum(da.zeros(4, chunks=1)).compute()
     y = np.sum(np.zeros(4))
     assert type(x) == type(y)
+
+
+def test_reduction_on_scalar():
+    x = da.from_array(np.array(1.0), chunks=())
+    assert (x == x).all()
Repository: dask/dask

Issue:
ProgressBar is not visible in the notebook
The `ProgressBar` doesn't update itself during execution while in the notebook.  Afterwards the full bar will pop up but it doesn't give you any cues during execution.



Discussion:
Here was my testing cell

``` python
from dask.diagnostics import ProgressBar
from dask.async import get_sync
from time import sleep

def f(x):
    sleep(x)
    return x

dsk = {('x', i): (f, 0.2) for i in range(10)}

with ProgressBar():
    get_sync(dsk, list(dsk))
```

Weird, it's because of the background thread. I specifically tested it in notebooks before originally pushing it. Commit 6d6fb216e works.

This explains it a bit more in detail: http://mail.scipy.org/pipermail/ipython-dev/2014-March/013614.html

If you add a call to `sys.stdout.flush()` in `f(x)`, then the progressbar updates after each function completion. This is because zmq sockets aren't threadsafe, so the output from the background thread is buffered until the kernel is idle. The call to `sys.stdout.flush()` clears that buffer.

A few options:
- Revert back to my original implementation with no timer
- Revert back to my original implementation, but add a timer that updates only when a task completes. This will be irregular, and may have large pauses for long running tasks.
- Some magic I haven't thought of yet.

What about flushing within the post task callback?  This would hopefully be a noop in the console and would default to irregular printing in the notebook.


Resolving patch:
diff --git a/dask/diagnostics/progress.py b/dask/diagnostics/progress.py
--- a/dask/diagnostics/progress.py
+++ b/dask/diagnostics/progress.py
@@ -54,6 +54,7 @@ def _start(self, dsk, state):
 
     def _posttask(self, key, value, dsk, state, id):
         self._ndone += 1
+        sys.stdout.flush()
 
     def _finish(self, dsk, state, errored):
         self._running = False


Test patch:
Repository: dask/dask

Issue:
Specify the name of a map_blocks task
It would be nice to be able to do `arr.map_blocks(f, name='my_function')`. When using callbacks, it would allow one to know which function is being executed in the current task.



Discussion:
Yeah, that's doable.


Resolving patch:
diff --git a/dask/array/core.py b/dask/array/core.py
--- a/dask/array/core.py
+++ b/dask/array/core.py
@@ -423,6 +423,11 @@ def map_blocks(func, *arrs, **kwargs):
 
     >>> def func(block, block_id=None):
     ...     pass
+
+    You may specify the name of the resulting task in the graph with the
+    optional ``name`` keyword argument.
+
+    >>> y = x.map_blocks(lambda x: x + 1, name='increment')
     """
     if not callable(func):
         raise TypeError("First argument must be callable function, not %s\n"
@@ -437,7 +442,8 @@ def map_blocks(func, *arrs, **kwargs):
 
     out_ind = tuple(range(max(x.ndim for x in arrs)))[::-1]
 
-    result = atop(func, out_ind, *args, dtype=dtype)
+    name = kwargs.get('name', None)
+    result = atop(func, out_ind, *args, name=name, dtype=dtype)
 
     # If func has block_id as an argument then swap out func
     # for func with block_id partialed in
@@ -1043,8 +1049,8 @@ def vnorm(self, ord=None, axis=None, keepdims=False):
         return vnorm(self, ord=ord, axis=axis, keepdims=keepdims)
 
     @wraps(map_blocks)
-    def map_blocks(self, func, chunks=None, dtype=None):
-        return map_blocks(func, self, chunks=chunks, dtype=dtype)
+    def map_blocks(self, func, chunks=None, dtype=None, name=None):
+        return map_blocks(func, self, chunks=chunks, dtype=dtype, name=name)
 
     def map_overlap(self, func, depth, boundary=None, trim=True, **kwargs):
         """ Map a function over blocks of the array with some overlap


Test patch:
diff --git a/dask/array/tests/test_array_core.py b/dask/array/tests/test_array_core.py
--- a/dask/array/tests/test_array_core.py
+++ b/dask/array/tests/test_array_core.py
@@ -593,6 +593,9 @@ def test_map_blocks():
     assert d.chunks == e.chunks
     assert eq(e, x + 1)
 
+    e = d.map_blocks(inc, name='increment')
+    assert e.name == 'increment'
+
     d = from_array(x, chunks=(10, 10))
     e = d.map_blocks(lambda x: x[::2, ::2], chunks=(5, 5), dtype=d.dtype)