Repository: arviz-devs/arviz

Issue:
from_pystan fails when there is empty dimension?
**Describe the bug**
`arviz.from_pystan` fails with IndexError when there is an empty-dimension parameter.

```python
az.from_pystan(stanfit)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-8-efb15a5798cb> in <module>
----> 1 az.from_pystan(stanfit)

~/.conda/envs/matrix/lib/python3.7/site-packages/arviz/data/io_pystan.py in from_pystan(posterior, posterior_predictive, prior, prior_predictive, observed_data, log_likelihood, coords, dims, posterior_model, prior_model)
    607             log_likelihood=log_likelihood,
    608             coords=coords,
--> 609             dims=dims,
    610         ).to_inference_data()

~/.conda/envs/matrix/lib/python3.7/site-packages/arviz/data/io_pystan.py in to_inference_data(self)
    152         return InferenceData(
    153             **{
--> 154                 "posterior": self.posterior_to_xarray(),
    155                 "sample_stats": self.sample_stats_to_xarray(),
    156                 "posterior_predictive": self.posterior_predictive_to_xarray(),

~/.conda/envs/matrix/lib/python3.7/site-packages/arviz/data/base.py in wrapped(cls, *args, **kwargs)
     23                 if getattr(cls, prop) is None:
     24                     return None
---> 25             return func(cls, *args, **kwargs)
     26 
     27         return wrapped

~/.conda/envs/matrix/lib/python3.7/site-packages/arviz/data/io_pystan.py in posterior_to_xarray(self)
     57         ignore = posterior_predictive + log_likelihood + ["lp__"]
     58 
---> 59         data = get_draws(posterior, ignore=ignore)
     60 
     61         return dict_to_dataset(data, library=self.pystan, coords=self.coords, dims=self.dims)

~/.conda/envs/matrix/lib/python3.7/site-packages/arviz/data/io_pystan.py in get_draws(fit, variables, ignore)
    403             for key, loc in keys_locs:
    404                 ary_slice = tuple(axes + loc)
--> 405                 ary[ary_slice] = pyholder.chains[key][-ndraw:]
    406         data[var] = ary
    407 

IndexError: index 3 is out of bounds for axis 2 with size 3
```
What I mean by empty-dimension parameter is:
```
>>> stanfit.par_dims
[[200], [3], [], [0, 3, 3], [200, 3], [0], [200, 3], [3, 3], [200, 3], [0]]
```

I think this is related to that I have this trick in my stan code:
```stan
parameters {
    ....
    matrix[3, 3] T[include_T ? 1 : 0];
}
```
I did this in order to add an optional parameter following [this](https://dev.to/martinmodrak/optional-parametersdata-in-stan-4o33).

**To Reproduce**
Sorry I cannot provide a self-contained example but the traceback is above and this only happens when include_T is 0 and thus T has shape [0, 3, 3] and maybe related to these issues: #576, #578.

I do not know if this is something arviz wants to support necessarily. But even if the answer is no, I was wondering if there is a workaround to easily instantiate InferenceData in this case.


**Expected behavior**
`az.from_pystan(stanfit)` returns the correct InferenceData object.

**Additional context**
arviz==0.4.0, pystan==2.18


Discussion:
Hi, thanks for the info. I was sure that I have fixed this, but apparently not.


Ok, my mistake

I have added 

    np.prod(dim) == 0

But

    np.prod([]) == 1.0

So I will fix this.
hmm, actually it might ve something else.
Yes, I found it.

It was this blunder (actually there was one other mistake)

    min((dim, i) for i, dim in enumerate(fit.sim["dims_oi"]) if dim)[1]

or in more simple terms

    min(dim for dim in fit.sim["dims_oi"] if dim)
which in your case resulted to `[0]` and then rest of the code fails.

I also needed to update one `if`-statement before that part, so everything should be fixed after I create a PR +merge.
Thanks for looking into it! I will wait for that.

Resolving patch:
diff --git a/arviz/data/io_pystan.py b/arviz/data/io_pystan.py
--- a/arviz/data/io_pystan.py
+++ b/arviz/data/io_pystan.py
@@ -355,9 +355,11 @@ def get_draws(fit, variables=None, ignore=None):
 
     # check if the values are in 0-based (<=2.17) or 1-based indexing (>=2.18)
     shift = 1
-    if any(fit.sim["dims_oi"]):
+    if any(dim and np.prod(dim) != 0 for dim in fit.sim["dims_oi"]):
         # choose variable with lowest number of dims > 1
-        par_idx = min((dim, i) for i, dim in enumerate(fit.sim["dims_oi"]) if dim)[1]
+        par_idx = min(
+            (dim, i) for i, dim in enumerate(fit.sim["dims_oi"]) if (dim and np.prod(dim) != 0)
+        )[1]
         offset = int(sum(map(np.product, fit.sim["dims_oi"][:par_idx])))
         par_offset = int(np.product(fit.sim["dims_oi"][par_idx]))
         par_keys = fit.sim["fnames_oi"][offset : offset + par_offset]


Test patch:
diff --git a/arviz/tests/test_data.py b/arviz/tests/test_data.py
--- a/arviz/tests/test_data.py
+++ b/arviz/tests/test_data.py
@@ -926,7 +926,9 @@ def test_empty_parameter(self):
             model_code = """
                 parameters {
                     real y;
-                    vector[0] z;
+                    vector[3] x;
+                    vector[0] a;
+                    vector[2] z;
                 }
                 model {
                     y ~ normal(0,1);
@@ -937,7 +939,7 @@ def test_empty_parameter(self):
             model = StanModel(model_code=model_code)
             fit = model.sampling(iter=10, chains=2, check_hmc_diagnostics=False)
             posterior = from_pystan(posterior=fit)
-            test_dict = {"posterior": ["y"], "sample_stats": ["lp"]}
+            test_dict = {"posterior": ["y", "x", "z"], "sample_stats": ["lp"]}
             fails = check_multiple_attrs(test_dict, posterior)
             assert not fails