Repository: pydata/sparse

Issue:
DOK to COO conversion: same dtype only when DOK array is not empty
I initialize a `DOK`-array with specific `dtype`,
don't set any value on it and convert it to a `COO`:

``` python
import numpy
import sparse

dok = sparse.DOK(1024, dtype=numpy.uint8)

coo = sparse.COO(dok)
```

The `dtype` of the `DOK` doesn't get applied to the `COO`.
```python
dok.dtype
>>> dtype('uint8')
coo.dtype
>>> dtype('float64')
```

This is only the case when I set a nonzero value on the `DOK` beforehand:

``` python
dok = sparse.DOK(1024, dtype=numpy.uint8)
dok[0] = 1
coo = sparse.COO(dok)

coo.dtype
>>> dtype('uint8')

coo.dtype == dok.dtype
>>> True
```

You might argue that instead I should use `sparse.zeros()` in the first place,
and while I generally agree, I think that the above behaviour is unexpected to me and should probably be changed.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pydata/sparse/pull/315?src=pr&el=h1) Report
> Merging [#315](https://codecov.io/gh/pydata/sparse/pull/315?src=pr&el=desc) into [master](https://codecov.io/gh/pydata/sparse/commit/6a0277959b1ae20d433ebab88334c28866a92a0e?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.


```diff
@@           Coverage Diff           @@
##           master     #315   +/-   ##
=======================================
  Coverage   93.93%   93.93%           
=======================================
  Files          19       19           
  Lines        2308     2308           
=======================================
  Hits         2168     2168           
  Misses        140      140
```




Patch:
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 607ba46..e3c2278 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -21,7 +21,7 @@ jobs:
   variables:
     python_version: '3.5'
   pool:
-    vmImage: 'macos-10.13'
+    vmImage: 'macos-10.14'
   steps:
     - template: ci/azure-steps.yml
 - job: Windows
diff --git a/setup.py b/setup.py
index 76f0430..5b02446 100755
--- a/setup.py
+++ b/setup.py
@@ -72,9 +72,7 @@ setup(
         "Tracker": "https://github.com/pydata/sparse/issues",
     },
     entry_points={
-        "numba_extensions": [
-            "init = sparse._numba_extension:_init_extension",
-        ],
+        "numba_extensions": ["init = sparse._numba_extension:_init_extension"]
     },
     python_requires=">=3.5, <4",
 )
diff --git a/sparse/_coo/core.py b/sparse/_coo/core.py
index 66080d3..ebd4766 100644
--- a/sparse/_coo/core.py
+++ b/sparse/_coo/core.py
@@ -445,7 +445,7 @@ class COO(SparseArray, NDArrayOperatorsMixin):  # lgtm [py/missing-equals]
         )
 
     @classmethod
-    def from_iter(cls, x, shape=None, fill_value=None):
+    def from_iter(cls, x, shape=None, fill_value=None, dtype=None):
         """
         Converts an iterable in certain formats to a :obj:`COO` array. See examples
         for details.
@@ -458,6 +458,8 @@ class COO(SparseArray, NDArrayOperatorsMixin):  # lgtm [py/missing-equals]
             The shape of the array.
         fill_value : scalar
             The fill value for this array.
+        dtype : numpy.dtype
+            The dtype of the input array. Inferred from the input if not given.
 
         Returns
         -------
@@ -511,15 +513,15 @@ class COO(SparseArray, NDArrayOperatorsMixin):  # lgtm [py/missing-equals]
         if not x:
             ndim = 0 if shape is None else len(shape)
             coords = np.empty((ndim, 0), dtype=np.uint8)
-            data = np.empty((0,))
+            data = np.empty((0,), dtype=dtype)
             shape = () if shape is None else shape
 
         elif not isinstance(x[0][0], Iterable):
             coords = np.stack(x[1], axis=0)
-            data = np.asarray(x[0])
+            data = np.asarray(x[0], dtype=dtype)
         else:
             coords = np.array([item[0] for item in x]).T
-            data = np.array([item[1] for item in x])
+            data = np.array([item[1] for item in x], dtype=dtype)
 
         if not (
             coords.ndim == 2
diff --git a/sparse/_dok.py b/sparse/_dok.py
index 2ddeeca..5e43df5 100644
--- a/sparse/_dok.py
+++ b/sparse/_dok.py
@@ -386,7 +386,10 @@ class DOK(SparseArray):
 
         if format == "coo" or format is COO:
             return COO.from_iter(
-                self.data, shape=self.shape, fill_value=self.fill_value
+                self.data,
+                shape=self.shape,
+                fill_value=self.fill_value,
+                dtype=self.dtype,
             )
 
         raise NotImplementedError("The given format is not supported.")


Test patch:
diff --git a/sparse/tests/test_dok.py b/sparse/tests/test_dok.py
index f855f8f..e670684 100644
--- a/sparse/tests/test_dok.py
+++ b/sparse/tests/test_dok.py
@@ -152,3 +152,9 @@ def test_coo_fv_interface():
     assert_eq(s1, s2)
     s3 = sparse.COO(s2)
     assert_eq(s1, s3)
+
+
+def test_empty_dok_dtype():
+    d = sparse.DOK(5, dtype=np.uint8)
+    s = sparse.COO(d)
+    assert s.dtype == d.dtype