Repository: arviz-devs/arviz

Issue:
Categorical x apparently not supported by arviz.plot_hdi()
Hello arviz-devs! Thanks for a wonderful package. This issue comes from a [discussion on the PyMC discourse](https://discourse.pymc.io/t/unexpected-behavior-with-arviz-plot-hdi-with-categorical-x/16403) including @tomicapretto and I. 

**Describe the bug**
To my understanding, [plot_hdi()](https://github.com/arviz-devs/arviz/blob/main/arviz/plots/hdiplot.py) does not currently support categorical x values (see code below), but this isn't explicitly noted in the documentation nor is there a ValueError or TypeError raised. Further, the functions default of `smooth=True` throws it's own error when a user passes a categorical x.

**To Reproduce**
```Python
import arviz as az
import bambi as bmb
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# Simulate data
np.random.seed(42)
x = ['A', 'B', 'C']
yA = np.random.normal(loc=5, scale=3, size=30)
yB = np.random.normal(loc=2, scale=4, size=30)
yC = np.random.normal(loc=7, scale=1.8, size=30)

# Create a DataFrame
data = pd.DataFrame({
    'y': np.concatenate([yA, yB, yC]),
    'group': np.repeat(x, repeats=30)
})
data['group'] = data['group'].astype('category')

# Plot the data
plt.figure(figsize=(8, 6))
sns.boxplot(x='group', y='y', data=data, palette='Set3')
sns.stripplot(x='group', y='y', data=data, color='black', alpha=0.5, jitter=True)
plt.title('Distribution of y across groups')
plt.xlabel('Group')
plt.ylabel('y')
plt.show()

```

![Image](https://github.com/user-attachments/assets/5474a535-4523-4263-9cbe-3d99349e55a3)

```Python
# Fit a Bayesian ANOVA model using Bambi
model = bmb.Model('y ~ group', data)
idata = model.fit()

preds = model.predict(idata, kind="response_params", inplace=False)
y_mu = az.extract(preds["posterior"])["mu"].values
group = data.loc[:, "group"].values

az.plot_hdi(x=group, y=y_mu.T)

```
Returns: 

> #UFuncTypeError: ufunc 'multiply' did not contain a loop with signature matching types (dtype('<U1'), dtype('float64')) -> None

The traceback points to `np.linspace` under the `if smooth:` block:
https://github.com/arviz-devs/arviz/blob/0fc11178e3802de9e2e6557ce455cced9a22974f/arviz/plots/hdiplot.py#L171-L182

Setting smooth to **False** does not return an expected plot:
```Python
az.plot_hdi(x=group, y=y_mu.T, smooth=False)
```

![Image](https://github.com/user-attachments/assets/b6441c4d-05f2-4ef2-bbfd-f3db3df536ee)

**Expected behavior**
Given the documentation, I'd expect the behavior to mirror the output from `bambi.interpret`

```Python
bmb.interpret.plot_predictions(
    model=model,
    idata=idata,
    conditional="group",
);
```

![Image](https://github.com/user-attachments/assets/e598f354-3425-482e-bf4c-11479afb6314)

**Additional context**
arviz 0.20.0 via conda-forge
Python 3.12.0

---

I think a TypeError informing the user of the lack of support for categorical (str) types would be very helpful to future users. 



Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4798a43ec7..687b1db477 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 - Only emit a warning for custom groups in `InferenceData` when explicitly requested ([2401](https://github.com/arviz-devs/arviz/pull/2401))
 - Splits Bayes Factor computation out from `az.plot_bf` into `az.bayes_factor` ([2402](https://github.com/arviz-devs/arviz/issues/2402))
 - Update `method="sd"` of `mcse` to not use normality assumption ([2167](https://github.com/arviz-devs/arviz/pull/2167)) 
+- Add exception in `az.plot_hdi` for `x` of type `str` ([2413](https://github.com/arviz-devs/arviz/pull/2413))
 
 ### Documentation
 - Add example of ECDF comparison plot to gallery ([2178](https://github.com/arviz-devs/arviz/pull/2178))
diff --git a/arviz/plots/hdiplot.py b/arviz/plots/hdiplot.py
index 196a1f53d9..546281634f 100644
--- a/arviz/plots/hdiplot.py
+++ b/arviz/plots/hdiplot.py
@@ -136,6 +136,11 @@ def plot_hdi(
     x = np.asarray(x)
     x_shape = x.shape
 
+    if isinstance(x[0], str):
+        raise NotImplementedError(
+            "The `arviz.plot_hdi()` function does not support categorical data. "
+            "Consider using `arviz.plot_forest()`."
+        )
     if y is None and hdi_data is None:
         raise ValueError("One of {y, hdi_data} is required")
     if hdi_data is not None and y is not None:


Test patch:
diff --git a/arviz/tests/base_tests/test_plots_matplotlib.py b/arviz/tests/base_tests/test_plots_matplotlib.py
index c2646a73f1..fcc548e9e7 100644
--- a/arviz/tests/base_tests/test_plots_matplotlib.py
+++ b/arviz/tests/base_tests/test_plots_matplotlib.py
@@ -2,21 +2,22 @@
 
 # pylint: disable=redefined-outer-name,too-many-lines
 import os
+import re
 from copy import deepcopy
 
 import matplotlib.pyplot as plt
 import numpy as np
 import pytest
+import xarray as xr
 from matplotlib import animation
 from pandas import DataFrame
 from scipy.stats import gaussian_kde, norm
-import xarray as xr
 
 from ...data import from_dict, load_arviz_data
 from ...plots import (
     plot_autocorr,
-    plot_bpv,
     plot_bf,
+    plot_bpv,
     plot_compare,
     plot_density,
     plot_dist,
@@ -43,20 +44,20 @@
     plot_ts,
     plot_violin,
 )
+from ...plots.dotplot import wilkinson_algorithm
+from ...plots.plot_utils import plot_point_interval
 from ...rcparams import rc_context, rcParams
 from ...stats import compare, hdi, loo, waic
 from ...stats.density_utils import kde as _kde
-from ...utils import _cov, BehaviourChangeWarning
-from ...plots.plot_utils import plot_point_interval
-from ...plots.dotplot import wilkinson_algorithm
+from ...utils import BehaviourChangeWarning, _cov
 from ..helpers import (  # pylint: disable=unused-import
+    RandomVariableTestClass,
     create_model,
     create_multidimensional_model,
     does_not_warn,
     eight_schools_params,
     models,
     multidim_models,
-    RandomVariableTestClass,
 )
 
 rcParams["data.load"] = "eager"
@@ -1236,6 +1237,23 @@ def test_plot_hdi_dataset_error(models):
         plot_hdi(np.arange(8), hdi_data=hdi_data)
 
 
+def test_plot_hdi_string_error():
+    """Check x as type string raises an error."""
+    x_data = ["a", "b", "c", "d"]
+    y_data = np.random.normal(0, 5, (1, 200, len(x_data)))
+    hdi_data = hdi(y_data)
+    with pytest.raises(
+        NotImplementedError,
+        match=re.escape(
+            (
+                "The `arviz.plot_hdi()` function does not support categorical data. "
+                "Consider using `arviz.plot_forest()`."
+            )
+        ),
+    ):
+        plot_hdi(x=x_data, y=y_data, hdi_data=hdi_data)
+
+
 def test_plot_hdi_datetime_error():
     """Check x as datetime raises an error."""
     x_data = np.arange(start="2022-01-01", stop="2022-03-01", dtype=np.datetime64)