Repository: dask/dask

Issue:
histogram range error when array has nans
I have encountered a confusing error when computing a histogram with specified bins and range on an array that contains nans. Dask is raising `ValueError: range parameter must be finite.` even when the range is specified

Consider the following numpy code:
```python
import numpy as np
a = np.array([-2, np.nan, 2])
nbins = 3 
hrange = (-3, 3)
np.histogram(a, bins=nbins, range=hrange)
```
```
(array([1, 0, 1]), array([-3., -1.,  1.,  3.]))
```
The nan value is simply ignored as out of range. (Numpy raises warnings about `invalid value encountered in less_equal` but it still works.)

Now we try the same thing with dask
```python
h, b = dsa.histogram(dsa.from_array(a, chunks=1), bins=nbins, range=hrange)
h.compute(), b
```

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-148-caf9ec164a84> in <module>()
      1 h, b = dsa.histogram(dsa.from_array(a, chunks=1), bins=nbins, range=hrange)
----> 2 h.compute(), b

/opt/conda/lib/python3.6/site-packages/dask/base.py in compute(self, **kwargs)
    154         dask.base.compute
    155         """
--> 156         (result,) = compute(self, traverse=False, **kwargs)
    157         return result
    158 

... bunch of distributed stuff ...

/opt/conda/lib/python3.6/site-packages/dask/array/routines.py in block_hist()
    576     # Map the histogram to all bins
    577     def block_hist(x, weights=None):
--> 578         return np.histogram(x, bins, weights=weights)[0][np.newaxis]
    579 
    580     if weights is None:

/opt/conda/lib/python3.6/site-packages/numpy/lib/function_base.py in histogram()
    668     if not np.all(np.isfinite([first_edge, last_edge])):
    669         raise ValueError(
--> 670             'range parameter must be finite.')
    671     if first_edge == last_edge:
    672         first_edge -= 0.5

ValueError: range parameter must be finite.

```

This suggests to me that the range parameter is somehow being ignored / not passed correctly to numpy.


Discussion:

Resolving patch:
diff --git a/dask/array/routines.py b/dask/array/routines.py
--- a/dask/array/routines.py
+++ b/dask/array/routines.py
@@ -627,17 +627,17 @@ def histogram(a, bins=None, range=None, normed=False, weights=None, density=None
     name = 'histogram-sum-' + token
 
     # Map the histogram to all bins
-    def block_hist(x, weights=None):
-        return np.histogram(x, bins, weights=weights)[0][np.newaxis]
+    def block_hist(x, range=None, weights=None):
+        return np.histogram(x, bins, range=range, weights=weights)[0][np.newaxis]
 
     if weights is None:
-        dsk = {(name, i, 0): (block_hist, k)
+        dsk = {(name, i, 0): (block_hist, k, range)
                for i, k in enumerate(flatten(a.__dask_keys__()))}
         dtype = np.histogram([])[0].dtype
     else:
         a_keys = flatten(a.__dask_keys__())
         w_keys = flatten(weights.__dask_keys__())
-        dsk = {(name, i, 0): (block_hist, k, w)
+        dsk = {(name, i, 0): (block_hist, k, range, w)
                for i, (k, w) in enumerate(zip(a_keys, w_keys))}
         dtype = weights.dtype
 


Test patch:
diff --git a/dask/array/tests/test_routines.py b/dask/array/tests/test_routines.py
--- a/dask/array/tests/test_routines.py
+++ b/dask/array/tests/test_routines.py
@@ -552,6 +552,15 @@ def test_histogram_alternative_bins_range():
     assert_eq(b1, b2)
 
 
+def test_histogram_bins_range_with_nan_array():
+    # Regression test for issue #3977
+    v = da.from_array(np.array([-2, np.nan, 2]), chunks=1)
+    (a1, b1) = da.histogram(v, bins=10, range=(-3, 3))
+    (a2, b2) = np.histogram(v, bins=10, range=(-3, 3))
+    assert_eq(a1, a2)
+    assert_eq(b1, b2)
+
+
 def test_histogram_return_type():
     v = da.random.random(100, chunks=10)
     bins = np.arange(0, 1.01, 0.01)