Repository: arviz-devs/arviz

Issue:
Posterior plot errors with boolean array
When passed a boolean array, `plot_posterior` fails with the following error because `np.histogram` assumes that subtraction is defined on the array:
```python
TypeError('numpy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.')
```

This example reproduces the error:

```python
import numpy as np
import arviz as az

data = np.random.choice(a=[False, True], size=(4, 100))
az.plot_posterior(data)
```


Discussion:
Just to be clear, the expected output would be a histogram with two bins, one around 0 and another around 1 (in which case we can add an `astype(int)` before the histogram and be done with it)? Or should the labels be `True` and `False`?
I'd expect the second, even though the first option is easier.
Then I think it should be something like:

1. Evaluate and store condition of whether or not the dtype is boolean
2. If True, do the `astype(int)` for histogram to work
3. (plotting should not require changes, just here for completeness)
4. If True, change the plot labels from 0 and 1 to False and True. Then reset the variable storing the condition 
We should use bar graph with counts for bool data, not hist.
> 3\. plotting should not require changes, just here for completeness

I guess this means plots/posteriorplot.py need to be edited and not plots/backends/matplotlib/posteriorplot.py and plots/backends/bokeh/posteriorplot.py ..?
User api should not change. Feel free to edit backend files.
Do we need `**bar_kwargs` plot bar plots?
@OriolAbril Can you please assign this issue to me?
I have added some lines of code and submitting a pr to discuss the problems.
done, in practice, especially with gsoc coming, you can already consider yourself assigned if you are the first to comment on the issue that you'll work on it, even if we take some time to assign you via the gitub assign feature. We already have a couple lines on that on the [contributing guide](https://github.com/arviz-devs/arviz/blob/main/CONTRIBUTING.md#if-an-issue-ticket-exists) for agility when it comes to contributing.

Resolving patch:
diff --git a/arviz/plots/backends/bokeh/posteriorplot.py b/arviz/plots/backends/bokeh/posteriorplot.py
--- a/arviz/plots/backends/bokeh/posteriorplot.py
+++ b/arviz/plots/backends/bokeh/posteriorplot.py
@@ -286,18 +286,30 @@ def format_axes():
             show=False,
         )
         _, hist, edges = histogram(values, bins="auto")
-    else:
+    elif values.dtype.kind == "i" or (values.dtype.kind == "f" and kind == "hist"):
         if bins is None:
-            if values.dtype.kind == "i":
-                bins = get_bins(values)
-            else:
-                bins = "auto"
+            bins = get_bins(values)
         kwargs.setdefault("align", "left")
         kwargs.setdefault("color", "blue")
         _, hist, edges = histogram(values, bins=bins)
         ax.quad(
             top=hist, bottom=0, left=edges[:-1], right=edges[1:], fill_alpha=0.35, line_alpha=0.35
         )
+    elif values.dtype.kind == "b":
+        if bins is None:
+            bins = "auto"
+        kwargs.setdefault("color", "blue")
+
+        hist = np.array([(~values).sum(), values.sum()])
+        edges = np.array([-0.5, 0.5, 1.5])
+        ax.quad(
+            top=hist, bottom=0, left=edges[:-1], right=edges[1:], fill_alpha=0.35, line_alpha=0.35
+        )
+        hdi_prob = "hide"
+        ax.xaxis.ticker = [0, 1]
+        ax.xaxis.major_label_overrides = {0: "False", 1: "True"}
+    else:
+        raise TypeError("Values must be float, integer or boolean")
 
     format_axes()
     max_data = hist.max()
diff --git a/arviz/plots/backends/matplotlib/posteriorplot.py b/arviz/plots/backends/matplotlib/posteriorplot.py
--- a/arviz/plots/backends/matplotlib/posteriorplot.py
+++ b/arviz/plots/backends/matplotlib/posteriorplot.py
@@ -319,18 +319,23 @@ def format_axes():
             rug=False,
             show=False,
         )
-    else:
+    elif values.dtype.kind == "i" or (values.dtype.kind == "f" and kind == "hist"):
         if bins is None:
-            if values.dtype.kind == "i":
-                xmin = values.min()
-                xmax = values.max()
-                bins = get_bins(values)
-                ax.set_xlim(xmin - 0.5, xmax + 0.5)
-            else:
-                bins = "auto"
+            xmin = values.min()
+            xmax = values.max()
+            bins = get_bins(values)
+            ax.set_xlim(xmin - 0.5, xmax + 0.5)
         kwargs.setdefault("align", "left")
         kwargs.setdefault("color", "C0")
         ax.hist(values, bins=bins, alpha=0.35, **kwargs)
+    elif values.dtype.kind == "b":
+        if bins is None:
+            bins = "auto"
+        kwargs.setdefault("color", "C0")
+        ax.bar(["False", "True"], [(~values).sum(), values.sum()], alpha=0.35, **kwargs)
+        hdi_prob = "hide"
+    else:
+        raise TypeError("Values must be float, integer or boolean")
 
     plot_height = ax.get_ylim()[1]
 


Test patch:
diff --git a/arviz/tests/base_tests/test_plots_bokeh.py b/arviz/tests/base_tests/test_plots_bokeh.py
--- a/arviz/tests/base_tests/test_plots_bokeh.py
+++ b/arviz/tests/base_tests/test_plots_bokeh.py
@@ -988,6 +988,17 @@ def test_plot_posterior_discrete(discrete_model, kwargs):
     assert axes.shape
 
 
+def test_plot_posterior_boolean():
+    data = np.random.choice(a=[False, True], size=(4, 100))
+    axes = plot_posterior(data, backend="bokeh", show=False)
+    assert axes.shape
+
+
+def test_plot_posterior_bad_type():
+    with pytest.raises(TypeError):
+        plot_posterior(np.array(["a", "b", "c"]), backend="bokeh", show=False)
+
+
 def test_plot_posterior_bad(models):
     with pytest.raises(ValueError):
         plot_posterior(models.model_1, backend="bokeh", show=False, rope="bad_value")
diff --git a/arviz/tests/base_tests/test_plots_matplotlib.py b/arviz/tests/base_tests/test_plots_matplotlib.py
--- a/arviz/tests/base_tests/test_plots_matplotlib.py
+++ b/arviz/tests/base_tests/test_plots_matplotlib.py
@@ -1025,12 +1025,26 @@ def test_plot_posterior(models, kwargs):
         assert axes.shape
 
 
+def test_plot_posterior_boolean():
+    data = np.random.choice(a=[False, True], size=(4, 100))
+    axes = plot_posterior(data)
+    assert axes
+    plt.draw()
+    labels = [label.get_text() for label in axes.get_xticklabels()]
+    assert all(item in labels for item in ("True", "False"))
+
+
 @pytest.mark.parametrize("kwargs", [{}, {"point_estimate": "mode"}, {"bins": None, "kind": "hist"}])
 def test_plot_posterior_discrete(discrete_model, kwargs):
     axes = plot_posterior(discrete_model, **kwargs)
     assert axes.shape
 
 
+def test_plot_posterior_bad_type():
+    with pytest.raises(TypeError):
+        plot_posterior(np.array(["a", "b", "c"]))
+
+
 def test_plot_posterior_bad(models):
     with pytest.raises(ValueError):
         plot_posterior(models.model_1, rope="bad_value")