Repository: scikit-hep/hist

Issue:
[BUG] SVG repr doesn’t support Weight storage
**Describe the bug**

I've already talked with @henryiii and so he has identified the Issue as described in the title, but I would not have known this from inspection as naively I assumed this was about `dtype` conversion. @henryiii had mentioned that there was a similar issue on `boost-histogram` but I'm not sure which one that is, so cc @HDembinski here too. @jpivarski has mentioned this is a combined issue that also will need coordination with `uproot4`.

**To Reproduce**

I [made a Gist](https://gist.github.com/matthewfeickert/2afe18c6b06ed14d889c3102513a621a) but you can also demo all of it in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gist/matthewfeickert/2afe18c6b06ed14d889c3102513a621a/master?urlpath=lab/tree/minimal-failing.ipynb)

```
$ git clone git@gist.github.com:2afe18c6b06ed14d889c3102513a621a.git hist-issue
$ cd hist-issue
$ python3 -m venv hist-issue
$ . hist-issue/bin/activate
(hist-issue) $ cat requirements.txt 
hist[plot]==2.0.1
uproot4==0.0.27
uproot~=3.12
jupyter~=1.0
jupyterlab~=2.2
(hist-issue) $ python -m pip install -q -r requirements.txt
(hist-issue) $ python minimal-failing.py
hists in file: ['mass;1']
Traceback (most recent call last):
  File "minimal-failing.py", line 100, in <module>
    main()
  File "minimal-failing.py", line 96, in main
    minimal_failing(root_file)
  File "minimal-failing.py", line 73, in minimal_failing
    hist_mass.plot()
  File "/home/feickert/.venvs/hist-issue/lib/python3.7/site-packages/hist/basehist.py", line 204, in plot
    return self.plot1d(*args, **kwargs)
  File "/home/feickert/.venvs/hist-issue/lib/python3.7/site-packages/hist/basehist.py", line 222, in plot1d
    return hist.plot.histplot(self, ax=ax, **kwargs)
  File "/home/feickert/.venvs/hist-issue/lib/python3.7/site-packages/mplhep/plot.py", line 132, in histplot
    h = np.asarray(h).astype(float)
TypeError: Cannot cast array data from dtype([('value', '<f8'), ('variance', '<f8')]) to dtype('float64') according to the rule 'unsafe'
```

where the TL;DR is

```python
import uproot4 as uproot
import hist
from hist import Hist

root_file = uproot.open("example.root")
hist_mass = root_file["mass"].to_hist()
hist_mass.plot()
```

errors and similarly in the Jupyter repr for

```python
hist_mass
```

one gets

```pytb
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/.venvs/JC-example/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    343             method = get_real_method(obj, self.print_method)
    344             if method is not None:
--> 345                 return method()
    346             return None
    347         else:

~/.venvs/JC-example/lib/python3.7/site-packages/hist/basehist.py in _repr_html_(self)
     64                 return str(html_hist(self, svg_hist_1d_c))
     65             else:
---> 66                 return str(html_hist(self, svg_hist_1d))
     67         elif self.ndim == 2:
     68             return str(html_hist(self, svg_hist_2d))

~/.venvs/JC-example/lib/python3.7/site-packages/hist/svgplots.py in html_hist(h, function)
     20 
     21 def html_hist(h, function):
---> 22     left_column = div(function(h), style="width:290px;")
     23     right_column = div(_desc_hist(h), style="flex=grow:1;")
     24 

~/.venvs/JC-example/lib/python3.7/site-packages/hist/svgplots.py in svg_hist_1d(h)
     53     (edges,) = h.axes.edges
     54     norm_edges = (edges - edges[0]) / (edges[-1] - edges[0])
---> 55     density = h.density()
     56     max_dens = np.max(density) or 1
     57     norm_vals = density / max_dens

~/.venvs/JC-example/lib/python3.7/site-packages/hist/basehist.py in density(self)
    187         Density numpy array.
    188         """
--> 189         total = self.sum() * functools.reduce(operator.mul, self.axes.widths)
    190         return self.view() / np.where(total > 0, total, 1)
    191 

TypeError: invalid type promotion
```

**Expected behavior**

The same output possible as 

```
(hist-issue) $ python minimal-failing.py pass
```

Discussion:

Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 77c373c..38adcb2 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,14 @@
 Changelog
 ====================
 
+
+Version 2.1.1
+--------------------
+
+* Fix density (and density based previews)
+  `#134 <https://github.com/scikit-hep/hist/pull/134>`_
+
+
 Version 2.1.0
 --------------------
 
@@ -26,7 +34,7 @@ Version 2.0.1
 * Fixed ``plot2d_full`` incorrectly mirroring the y-axis.
   `#105 <https://github.com/scikit-hep/hist/pull/105>`_
 
-* `Hist.plot_pull`: more suitable bands in the pull bands 1sigma, 2 sigma, etc.
+* ``Hist.plot_pull``: more suitable bands in the pull bands 1sigma, 2 sigma, etc.
   `#102 <https://github.com/scikit-hep/hist/pull/102>`_
 
 * Fixed classichist's usage of `get_terminal_size` to support not running in a terminal
diff --git a/src/hist/basehist.py b/src/hist/basehist.py
index 26afbf1..e24df4b 100644
--- a/src/hist/basehist.py
+++ b/src/hist/basehist.py
@@ -214,7 +214,7 @@ class BaseHist(bh.Histogram, metaclass=MetaConstructor):
         """
         Density numpy array.
         """
-        total = self.sum() * functools.reduce(operator.mul, self.axes.widths)
+        total = np.sum(self.values()) * functools.reduce(operator.mul, self.axes.widths)
         return self.values() / np.where(total > 0, total, 1)
 
     def show(self, **kwargs):


Test patch:
diff --git a/tests/test_general.py b/tests/test_general.py
index 627527e..e59c315 100644
--- a/tests/test_general.py
+++ b/tests/test_general.py
@@ -759,6 +759,14 @@ def test_general_density():
         assert pytest.approx(sum(h.density()), 2) == pytest.approx(10 / 6, 2)
 
 
+def test_weighted_density():
+    for data in range(10, 20, 10):
+        h = Hist(axis.Regular(10, -3, 3, name="x"), storage="weight").fill(
+            np.random.randn(data)
+        )
+        assert pytest.approx(sum(h.density()), 2) == pytest.approx(10 / 6, 2)
+
+
 def test_general_axestuple():
     """
     Test general axes tuple -- whether Hist axes tuple work properly.