Repository: dask/dask

Issue:
stack/concatenate duplicates last result in list comprehension
Am running into some strange behavior with Dask `stack` and `concatenate` where they end up duplicating the last value in a list comprehension for all other values in the stacked or concatenated array. Not seeing this across the board, but am seeing it reliably in a few cases. Not sure if there is some optimization gone awry or something else giving rise to this problem. Have documented it in issue ( https://github.com/dask-image/dask-ndfilters/issues/28 ) with an example and specs for reproducing the issue.

Discussion:
jcrist: We may want to rethink these keyword names at some point. It'd be a bit of a pain to deprecate since this is public api, but the current keywords aren't the clearest (existing for historical reasons).

If I was to redo them I'd probably have `key_name` be for specifying the full key (`name` currently), and `key_prefix` for just the prefix (`token` currently). If we were to change them we'd probably want to mirror this convention in `dask.dataframe` and `dask.bag` as well.

Patch:
diff --git a/dask/array/core.py b/dask/array/core.py
index 0f602a296..5e6830bee 100644
--- a/dask/array/core.py
+++ b/dask/array/core.py
@@ -533,9 +533,13 @@ def map_blocks(func, *args, **kwargs):
     new_axis : number or iterable, optional
         New dimensions created by the function. Note that these are applied
         after ``drop_axis`` (if present).
+    token : string, optional
+        The key prefix to use for the output array. If not provided, will be
+        determined from the function name.
     name : string, optional
-        The key name to use for the array. If not provided, will be determined
-        by a hash of the arguments.
+        The key name to use for the output array. Note that this fully
+        specifies the output key name, and must be unique. If not provided,
+        will be determined by a hash of the arguments.
     **kwargs :
         Other keyword arguments to pass to function. Values must be constants
         (not dask.arrays)
@@ -608,10 +612,11 @@ def map_blocks(func, *args, **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.
+    You may specify the key name prefix of the resulting task in the graph with
+    the optional ``token`` keyword argument.
 
-    >>> y = x.map_blocks(lambda x: x + 1, name='increment')
+    >>> x.map_blocks(lambda x: x + 1, token='increment')  # doctest: +SKIP
+    dask.array<increment, shape=(100,), dtype=int64, chunksize=(10,)>
     """
     if not callable(func):
         msg = ("First argument must be callable function, not %s\n"
@@ -619,7 +624,10 @@ def map_blocks(func, *args, **kwargs):
                "   or:   da.map_blocks(function, x, y, z)")
         raise TypeError(msg % type(func).__name__)
     name = kwargs.pop('name', None)
-    name = name or '%s-%s' % (funcname(func), tokenize(func, args, **kwargs))
+    token = kwargs.pop('token', None)
+    if not name:
+        name = '%s-%s' % (token or funcname(func),
+                          tokenize(token or func, args, **kwargs))
     dtype = kwargs.pop('dtype', None)
     chunks = kwargs.pop('chunks', None)
     drop_axis = kwargs.pop('drop_axis', [])
diff --git a/dask/array/random.py b/dask/array/random.py
index 7bed2acd7..ad5ef1954 100644
--- a/dask/array/random.py
+++ b/dask/array/random.py
@@ -37,7 +37,7 @@ class RandomState(object):
     >>> state = da.random.RandomState(1234)  # a seed
     >>> x = state.normal(10, 0.1, size=3, chunks=(2,))
     >>> x.compute()
-    array([ 10.06307943,   9.91493648,  10.0822082 ])
+    array([ 10.01867852,  10.04812289,   9.89649746])
 
     See Also:
         np.random.RandomState
diff --git a/dask/utils.py b/dask/utils.py
index 637f7bd82..2a007966c 100644
--- a/dask/utils.py
+++ b/dask/utils.py
@@ -263,7 +263,7 @@ def random_state_data(n, random_state=None):
     Parameters
     ----------
     n : int
-        Number of tuples to return.
+        Number of arrays to return.
     random_state : int or np.random.RandomState, optional
         If an int, is used to seed a new ``RandomState``.
     """
@@ -272,9 +272,10 @@ def random_state_data(n, random_state=None):
     if not isinstance(random_state, np.random.RandomState):
         random_state = np.random.RandomState(random_state)
 
-    maxuint32 = np.iinfo(np.uint32).max
-    return [(random_state.rand(624) * maxuint32).astype('uint32')
-            for i in range(n)]
+    random_data = random_state.bytes(624 * n * 4)  # `n * 624` 32-bit integers
+    l = list(np.frombuffer(random_data, dtype=np.uint32).reshape((n, -1)))
+    assert len(l) == n
+    return l
 
 
 def is_integer(i):
diff --git a/docs/source/array-creation.rst b/docs/source/array-creation.rst
index 5cfd4a4bf..86b466290 100644
--- a/docs/source/array-creation.rst
+++ b/docs/source/array-creation.rst
@@ -149,7 +149,7 @@ Unknown Chunks
 
 Some arrays have unknown chunk sizes.  These are designated using ``np.nan``
 rather than an integer.  These arrays support many but not all operations.  In
-particular, opeations like slicing are not possible and will result in an
+particular, operations like slicing are not possible and will result in an
 error.
 
 .. code-block:: python
diff --git a/docs/source/dataframe-create.rst b/docs/source/dataframe-create.rst
index 48709ec47..7d429c3a2 100644
--- a/docs/source/dataframe-create.rst
+++ b/docs/source/dataframe-create.rst
@@ -97,7 +97,7 @@ From Raw Dask Graphs
 This section is mainly for developers wishing to extend dask.dataframe.  It
 discusses internal API not normally needed by users.  Everything below can be
 done just as effectively with :doc:`dask.delayed<delayed-overview>`  described
-just above.  You should never need to create a dataframe object by han
+just above.  You should never need to create a dataframe object by hand.
 
 To construct a DataFrame manually from a dask graph you need the following
 information:
diff --git a/docs/source/dataframe-design.rst b/docs/source/dataframe-design.rst
index f23784bf8..f16b33bc1 100644
--- a/docs/source/dataframe-design.rst
+++ b/docs/source/dataframe-design.rst
@@ -81,7 +81,7 @@ Dask dataframe divides `categorical data`_ into two types:
   different categories in each partition.  Internally, unknown categoricals are
   indicated by the presence of ``dd.utils.UNKNOWN_CATEGORIES`` in the
   categories on the ``_meta`` attribute.  Since most dataframe operations
-  propogate the categories, the known/unknown status should propogate through
+  propagate the categories, the known/unknown status should propagate through
   operations (similar to how ``NaN`` propagates).
 
 For metadata specified as a description (option 2 above), unknown categoricals
diff --git a/docs/source/remote-data-services.rst b/docs/source/remote-data-services.rst
index 3871ba5bd..1cebacc37 100644
--- a/docs/source/remote-data-services.rst
+++ b/docs/source/remote-data-services.rst
@@ -148,7 +148,7 @@ The following parameters may be passed to s3fs using ``storage_options``:
 
     - token: if authentication has been done with some other S3 client
 
-    - use_ssl: whether connections are encryted and secure (default True)
+    - use_ssl: whether connections are encrypted and secure (default True)
 
     - client_kwargs: dict passed to the `boto3 client`_, with keys such
       as `region_name`, `endpoint_url`


Test patch:
diff --git a/dask/array/tests/test_array_core.py b/dask/array/tests/test_array_core.py
index 5ab285d6a..c4e0d5d68 100644
--- a/dask/array/tests/test_array_core.py
+++ b/dask/array/tests/test_array_core.py
@@ -895,6 +895,10 @@ def test_map_blocks():
     e = d.map_blocks(inc, name='increment')
     assert e.name == 'increment'
 
+    e = d.map_blocks(inc, token='increment')
+    assert e.name != 'increment'
+    assert e.name.startswith('increment')
+
     d = from_array(x, chunks=(10, 10))
     e = d.map_blocks(lambda x: x[::2, ::2], chunks=(5, 5), dtype=d.dtype)
 
diff --git a/dask/tests/test_utils.py b/dask/tests/test_utils.py
index c87033147..d9ff77c0a 100644
--- a/dask/tests/test_utils.py
+++ b/dask/tests/test_utils.py
@@ -59,11 +59,10 @@ def test_dispatch():
     assert foo((1, 2.0, b)) == (2, 1.0, b)
 
 
-@pytest.mark.slow
 def test_random_state_data():
     seed = 37
     state = np.random.RandomState(seed)
-    n = 100000
+    n = 10000
 
     # Use an integer
     states = random_state_data(n, seed)
@@ -72,6 +71,7 @@ def test_random_state_data():
     # Use RandomState object
     states2 = random_state_data(n, state)
     for s1, s2 in zip(states, states2):
+        assert s1.shape == (624,)
         assert (s1 == s2).all()
 
     # Consistent ordering