Repository: arviz-devs/arviz

Issue:
Ordering of variables after coords set
## Short Description

I'm trying to use the coords/dims to set labels on posterior variables from a stan fit and the order of the dimensions gets shuffled when I set the labels (not sure what determines the order). Is there a way to (1) retain the existing order (A,B,C...) and (2) sort the order according to a custom index that I provide (e.g. pass a index array like [2,1,0,3, ...] to have the plot appear in the order [C, B, A, D ...])? 

Sorry if this is covered in the documentation somewhere, I didn't see it.




Without setting any coords/dims, the variables appear in the same order as one would expect from the stan code.

![test1](https://user-images.githubusercontent.com/12652166/51454598-838ab300-1d13-11e9-9d8d-fd42e3be07b2.png)



Setting the coords/dims with the code below changes the order to this.

![test2](https://user-images.githubusercontent.com/12652166/51454545-3b6b9080-1d13-11e9-8417-6e9a029e8803.png)



## Code Example or link

```
azdata = az.from_pystan(posterior=fit, observed_data=['Y'], coords={'betas': ['A', 'B', 'C', 'D', 'E', 'F', 'G']}, dims={'b': ['betas']})
	
axes = az.plot_forest(azdata, var_names=('b'))
```
Using arviz 0.3.2



Discussion:
Thanks, this is definitely a bug. How is the order in `azdata`?

Are you on python 3.5 or 3.6+?
Same behavior on python 3.6 and 3.7 (numpy 1.15.4 in both cases). The order in the azdata variable looks fine as far as I can tell (let me know if that's not what you mean).

```
<xarray.Dataset>
Dimensions:               (b_Intercept_dim_0: 4, betas: 7, chain: 2, draw: 500, r_1_1_dim_0: 29, r_2_1_dim_0: 96, sd_1_dim_0: 1, sd_2_dim_0: 1, temp_Intercept_dim_0: 4, z_1_dim_0: 1, z_1_dim_1: 29, z_2_dim_0: 1, z_2_dim_1: 96)
Coordinates:
  * chain                 (chain) int64 0 1
  * draw                  (draw) int64 0 1 2 3 4 5 6 ... 494 495 496 497 498 499
  * betas                 (betas) <U1 'A' 'B' 'C' 'D' 'E' 'F' 'G'
  * temp_Intercept_dim_0  (temp_Intercept_dim_0) int64 0 1 2 3
  * sd_1_dim_0            (sd_1_dim_0) int64 0
  * z_1_dim_0             (z_1_dim_0) int64 0
  * z_1_dim_1             (z_1_dim_1) int64 0 1 2 3 4 5 6 ... 23 24 25 26 27 28
  * sd_2_dim_0            (sd_2_dim_0) int64 0
  * z_2_dim_0             (z_2_dim_0) int64 0
  * z_2_dim_1             (z_2_dim_1) int64 0 1 2 3 4 5 6 ... 90 91 92 93 94 95
  * r_1_1_dim_0           (r_1_1_dim_0) int64 0 1 2 3 4 5 ... 23 24 25 26 27 28
  * r_2_1_dim_0           (r_2_1_dim_0) int64 0 1 2 3 4 5 ... 90 91 92 93 94 95
  * b_Intercept_dim_0     (b_Intercept_dim_0) int64 0 1 2 3
Data variables:
    b                     (chain, draw, betas) float64 -1.356 0.6095 ... 0.5485
    temp_Intercept        (chain, draw, temp_Intercept_dim_0) float64 -1.338 ... 5.715
    sd_1                  (chain, draw, sd_1_dim_0) float64 2.041 ... 1.669
    z_1                   (chain, draw, z_1_dim_0, z_1_dim_1) float64 0.4641 ... 0.4383
    sd_2                  (chain, draw, sd_2_dim_0) float64 3.567 ... 3.731
    z_2                   (chain, draw, z_2_dim_0, z_2_dim_1) float64 -0.06457 ... 0.2636
    r_1_1                 (chain, draw, r_1_1_dim_0) float64 0.9473 ... 0.7318
    r_2_1                 (chain, draw, r_2_1_dim_0) float64 -0.2303 ... 0.9832
    b_Intercept           (chain, draw, b_Intercept_dim_0) float64 -1.401 ... 4.859
Attributes:
    created_at:                 2019-01-21T08:20:13.242255
    inference_library:          pystan
    inference_library_version:  2.18.1.0
```
@daeh Is the InferenceData object something you can share? Although not strictly necessary it would help me help you if I could get a copy of the input data 

Thank you for reporting this
Just to clarify: when you plot

```python
az.plot_forest(fit, var_names='b')
```

you get a different result from

```python
azdata = az.from_pystan(posterior=fit, observed_data=['Y'], coords={'betas': ['A', 'B', 'C', 'D', 'E', 'F', 'G']}, dims={'b': ['betas']})
az.plot_forest(azdata, var_names='b')
```

and specifically the coordinates show up out of order?
@canyon289 pickled inference objects attached [inferenceobjects.zip](https://github.com/arviz-devs/arviz/files/2781219/inferenceobjects.zip).

@ColCarroll correct.

Code that generated both below:

```
axes = az.plot_forest(fit, var_names=('b'))
plt.savefig('fit_raw.png')

##

azdata_base = az.from_pystan(posterior=fit, observed_data=['Y'], dims={'b': ['betas']})

axes = az.plot_forest(azdata_base, var_names=('b'))
plt.savefig('azdata_base.png')

with open('azdata_base.pkl', 'wb') as f:
	pickle.dump(azdata_base, f)

##

azdata = az.from_pystan(posterior=fit, observed_data=['Y'], coords={'betas': ['A', 'B', 'C', 'D', 'E', 'F', 'G']}, dims={'b': ['betas']})

axes = az.plot_forest(azdata, var_names=('b'))
plt.savefig('azdata.png')

with open('azdata.pkl', 'wb') as f:
	pickle.dump(azdata, f)
```


| raw stan fit  | azdata_base.png | azdata.png |
| ------------- | ------------- | ------------- |
| ![fit_raw](https://user-images.githubusercontent.com/12652166/51511509-91057300-1dcf-11e9-876e-66bef2c60f7a.png)  | ![azdata_base](https://user-images.githubusercontent.com/12652166/51511112-f9535500-1dcd-11e9-95a3-539991dcbfe6.png) | ![azdata](https://user-images.githubusercontent.com/12652166/51511110-f8babe80-1dcd-11e9-98fa-7d6b7907b92b.png)  |


Thanks for the prompt reply @daeh! (also, kudos for the sweet `pathlib` use!)

This can be recreated with the eight schools model in pymc3. I should be able to dig deeper tomorrow morning. Definitely a bug, but thank goodness that the dimensions map correctly (0 -> 'a', 1 -> 'b', etc)!

```python
import arviz as az
import numpy as np
import pymc3 as pm

data = {
    "J": 8,
    "y": np.array([28.0, 8.0, -3.0, 7.0, -1.0, 1.0, 18.0, 12.0]),
    "sigma": np.array([15.0, 10.0, 16.0, 11.0, 9.0, 11.0, 10.0, 18.0]),
}

with pm.Model() as model:
    mu = pm.Normal("mu", mu=0, sd=5)
    tau = pm.HalfCauchy("tau", beta=5)
    eta = pm.Normal("eta", mu=0, sd=1, shape=data["J"])
    theta = pm.Deterministic("theta", mu + tau * eta)
    pm.Normal("obs", mu=theta, sd=data["sigma"], observed=data["y"])
    trace = pm.sample()

az.plot_forest(trace, var_names='theta')
```
![image](https://user-images.githubusercontent.com/2295568/51511913-3e2cbb00-1dd1-11e9-8a69-6f65e42b9cde.png)


```python
data = az.from_pymc3(trace=trace, coords={'schools': list('abcdefgh')}, dims={'theta': ['schools']})
az.plot_forest(data, var_names='theta')
```
![image](https://user-images.githubusercontent.com/2295568/51511920-45ec5f80-1dd1-11e9-8984-f8c19edbfbb1.png)



Resolving patch:
diff --git a/arviz/plots/plot_utils.py b/arviz/plots/plot_utils.py
--- a/arviz/plots/plot_utils.py
+++ b/arviz/plots/plot_utils.py
@@ -260,7 +260,7 @@ def xarray_var_iter(data, var_names=None, combined=False, skip_dims=None, revers
     for var_name in var_names:
         if var_name in data:
             new_dims = [dim for dim in data[var_name].dims if dim not in skip_dims]
-            vals = [set(data[var_name][dim].values) for dim in new_dims]
+            vals = [np.unique(data[var_name][dim].values) for dim in new_dims]
             dims = [{k: v for k, v in zip(new_dims, prod)} for prod in product(*vals)]
             if reverse_selections:
                 dims = reversed(dims)


Test patch:
diff --git a/arviz/tests/test_plot_utils.py b/arviz/tests/test_plot_utils.py
--- a/arviz/tests/test_plot_utils.py
+++ b/arviz/tests/test_plot_utils.py
@@ -3,6 +3,7 @@
 import xarray as xr
 import pytest
 
+from ..data import from_dict
 from ..plots.plot_utils import make_2d, xarray_to_ndarray, xarray_var_iter, get_bins, get_coords
 
 
@@ -54,6 +55,19 @@ def test_dataset_to_numpy_combined(sample_dataset):
     assert (data[var_names.index("tau")] == tau.reshape(1, 6)).all()
 
 
+def test_xarray_var_iter_ordering():
+    """Assert that coordinate names stay the provided order"""
+    coords = list("abcd")
+    data = from_dict(  # pylint: disable=no-member
+        {"x": np.random.randn(1, 100, len(coords))},
+        coords={"in_order": coords},
+        dims={"x": ["in_order"]},
+    ).posterior
+
+    coord_names = [sel["in_order"] for _, sel, _ in xarray_var_iter(data)]
+    assert coord_names == coords
+
+
 def test_xarray_var_iter_ordering_combined(sample_dataset):  # pylint: disable=invalid-name
     """Assert that varname order stays consistent when chains are combined"""
     _, _, data = sample_dataset