Repository: arviz-devs/arviz

Issue:
arviz.summary fails with boolean array
**Describe the bug**
`arviz.summary` fails if one of the inputs is a boolean array.

**To Reproduce**
```python
import numpy as np
import pandas as pd
import arviz as az

d = pd.DataFrame(np.random.normal(size=(500, 3)),columns=['a', 'b', 'c'])
d['c'] = d['c'] > 0  # make column c boolean

az.summary(d.to_dict(orient='list'))
```
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-57-b7e6e7648733> in <module>
      6 d['c'] = d['c'] > 0  # make column c boolean
      7 
----> 8 az.summary(d.to_dict(orient='list'))

~/miniconda3/envs/py3/lib/python3.7/site-packages/arviz/stats/stats.py in summary(data, var_names, fmt, kind, round_to, include_circ, stat_funcs, extend, credible_interval, order, index_origin, skipna, coords, dims)
    970             kwargs=dict(credible_interval=credible_interval, multimodal=False, skipna=skipna),
    971             input_core_dims=(("chain", "draw"),),
--> 972             output_core_dims=tuple([] for _ in range(2)),
    973         )
    974 

~/miniconda3/envs/py3/lib/python3.7/site-packages/xarray/core/computation.py in apply_ufunc(func, input_core_dims, output_core_dims, exclude_dims, vectorize, join, dataset_join, dataset_fill_value, keep_attrs, kwargs, dask, output_dtypes, output_sizes, meta, *args)
   1053             dataset_join=dataset_join,
   1054             fill_value=dataset_fill_value,
-> 1055             keep_attrs=keep_attrs,
   1056         )
   1057     elif any(isinstance(a, DataArray) for a in args):

~/miniconda3/envs/py3/lib/python3.7/site-packages/xarray/core/computation.py in apply_dataset_vfunc(func, signature, join, dataset_join, fill_value, exclude_dims, keep_attrs, *args)
    380 
    381     result_vars = apply_dict_of_variables_vfunc(
--> 382         func, *args, signature=signature, join=dataset_join, fill_value=fill_value
    383     )
    384 

~/miniconda3/envs/py3/lib/python3.7/site-packages/xarray/core/computation.py in apply_dict_of_variables_vfunc(func, signature, join, fill_value, *args)
    325     result_vars = {}
    326     for name, variable_args in zip(names, grouped_by_name):
--> 327         result_vars[name] = func(*variable_args)
    328 
    329     if signature.num_outputs > 1:

~/miniconda3/envs/py3/lib/python3.7/site-packages/xarray/core/computation.py in apply_variable_ufunc(func, signature, exclude_dims, dask, output_dtypes, output_sizes, keep_attrs, meta, *args)
    602                 "apply_ufunc: {}".format(dask)
    603             )
--> 604     result_data = func(*input_data)
    605 
    606     if signature.num_outputs == 1:

~/miniconda3/envs/py3/lib/python3.7/site-packages/arviz/stats/stats_utils.py in _multi_ufunc(out, *args, **kwargs)
    152         for idx in np.ndindex(element_shape):
    153             arys_idx = [ary[idx].ravel() if ravel else ary[idx] for ary in arys]
--> 154             results = func(*arys_idx, *args[n_input:], **kwargs)
    155             for i, res in enumerate(results):
    156                 out[i][idx] = np.asarray(res)[index]

~/miniconda3/envs/py3/lib/python3.7/site-packages/arviz/stats/stats.py in hpd(ary, credible_interval, circular, multimodal, skipna)
    410         interval_idx_inc = int(np.floor(credible_interval * n))
    411         n_intervals = n - interval_idx_inc
--> 412         interval_width = ary[interval_idx_inc:] - ary[:n_intervals]
    413 
    414         if len(interval_width) == 0:

TypeError: numpy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.
```

**Expected behavior**
Should provide an output as if the boolean array is of type int (0/1)

**Additional context**
arviz 0.7.0



Discussion:
Hi, from the error trace, it looks like it fails trying to call `hpd`, can you confirm that `az.rhat` and `az.ess` do work and it is only `az.hpd` what fails?
Sure. You're correct that it errors on `hpd`, although the output of `rhat` is also all nan... not sure if it should be? Outputs below for clarity:

```python
print(az.ess(d.to_dict(orient='list')))
```
```
<xarray.Dataset>
Dimensions:  ()
Data variables:
    a        float64 454.0
    b        float64 362.7
    c        float64 453.5
```
```python
print(az.rhat(d.to_dict(orient='list')))
```
```
arviz.stats.stats_utils - WARNING - Shape validation failed: input_shape: (1, 500), minimum_shape: (chains=2, draws=4)
arviz.stats.stats_utils - WARNING - Shape validation failed: input_shape: (1, 500), minimum_shape: (chains=2, draws=4)
arviz.stats.stats_utils - WARNING - Shape validation failed: input_shape: (1, 500), minimum_shape: (chains=2, draws=4)
<xarray.Dataset>
Dimensions:  ()
Data variables:
    a        float64 nan
    b        float64 nan
    c        float64 nan
```
```python
print(az.hpd(d.to_dict(orient='list')))
```
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-52-02833eabe8cc> in <module>
----> 1 print(az.hpd(d.to_dict(orient='list')))

~/miniconda3/envs/py3/lib/python3.7/site-packages/arviz/stats/stats.py in hpd(ary, credible_interval, circular, multimodal, skipna)
    350             raise ValueError("The value of credible_interval should be in the interval (0, 1]")
    351 
--> 352     if ary.ndim > 1:
    353         hpd_array = np.array(
    354             [

AttributeError: 'dict' object has no attribute 'ndim'
```
Thanks @oscarbranson, this gives a detailed picture of the different possibilities. I think I have a clear idea of what has to be done in order to fix the issue. 

Regarding rhat, as there is only one chain present, it cannot be calculated, thus, in order to avoid erroring out `summary` or other functions like end of sampling report that call it behind the scenes, it logs a warning and returns nan. 

Regarding hpd, we should probably some checks before the calculation itself, in a similar manner to what rhat does. Moreover, for coherence, it should also do like ess and rhat and when the input is not an array call `convert_to_inference_data` or to dataset (in the second case it would not crash due to the input object being a dictionary). I also think that these initial checks should handle integers/booleans for which it generally makes no sense to compute the hpd cc @aloctavodia @agustinaarroyuelo 

Making `hpd` behave like `rhat`/`ess` should solve the issue with summary erroring out when one of the variables is boolean.

Resolving patch:
diff --git a/arviz/stats/stats.py b/arviz/stats/stats.py
--- a/arviz/stats/stats.py
+++ b/arviz/stats/stats.py
@@ -521,7 +521,7 @@ def _hdi(ary, hdi_prob, circular, skipna):
     ary = np.sort(ary)
     interval_idx_inc = int(np.floor(hdi_prob * n))
     n_intervals = n - interval_idx_inc
-    interval_width = ary[interval_idx_inc:] - ary[:n_intervals]
+    interval_width = np.subtract(ary[interval_idx_inc:], ary[:n_intervals], dtype=np.float_)
 
     if len(interval_width) == 0:
         raise ValueError("Too few elements for interval calculation. ")


Test patch: