Repository: arviz-devs/arviz

Issue:
Deprecated Frame.append and Series.append in Pandas
**Describe the bug**
FutureWarning about `.append()` deprecation. From pandas [documentation](https://pandas.pydata.org/docs/whatsnew/v1.4.0.html):
> DataFrame.append() and Series.append() have been deprecated and will be removed in Pandas 2.0. Use pandas.concat() instead (GH35407).

**To Reproduce**
```python
az.compare({
    'm1': trace_1,
    'm2: trace_2,
})
```

```python
~/miniconda3/lib/python3.9/site-packages/arviz/stats/stats.py:212: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead.
  ics = ics.append([ic_func(dataset, pointwise=True, scale=scale)])
~/miniconda3/lib/python3.9/site-packages/arviz/stats/stats.py:212: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead.
  ics = ics.append([ic_func(dataset, pointwise=True, scale=scale)])
```

**Additional context**
```
ArviZ version: 0.11.2
Pandas version: 1.4.0
```

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2eaa10c..ac41821 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
 * Add new convenience function `arviz.extract_dataset` ([1725](https://github.com/arviz-devs/arviz/pull/1725))
 * [experimental] Enable dask chunking information to be passed to `InferenceData.from_netcdf` with regex support ([1749](https://github.com/arviz-devs/arviz/pull/1749))
 * Allow kwargs to customize appearance of the mean in `plot_lm`
+* Add dict option to `from_cmdstan` log_likelihood parameter (as in `from_pystan`)
+
 
 ### Maintenance and fixes
 * Drop Python 3.6 support ([1430](https://github.com/arviz-devs/arviz/pull/1430))
@@ -12,6 +14,7 @@
 * Remove manual setting of 2d KDE limits ([1939](https://github.com/arviz-devs/arviz/pull/1939))
 * Pin to bokeh<3 version ([1954](https://github.com/arviz-devs/arviz/pull/1954))
 * Fix legend labels in plot_ppc to reflect prior or posterior. ([1967](https://github.com/arviz-devs/arviz/pull/1967))
+* Change `DataFrame.append` to `pandas.concat` ([1973](https://github.com/arviz-devs/arviz/pull/1973))
 
 ### Deprecation
 
diff --git a/MANIFEST.in b/MANIFEST.in
index 71eaf8c..c6a9848 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,6 +2,7 @@ include *.md
 include *.txt
 include *.toml
 include LICENSE
+include arviz/py.typed
 
 recursive-include arviz *.mplstyle
 recursive-include arviz *.nc
diff --git a/arviz/data/io_cmdstan.py b/arviz/data/io_cmdstan.py
index 8f61ab1..93c42d1 100644
--- a/arviz/data/io_cmdstan.py
+++ b/arviz/data/io_cmdstan.py
@@ -217,6 +217,12 @@ class CmdStanConverter:
             log_likelihood = []
         elif isinstance(log_likelihood, str):
             log_likelihood = [col for col in columns if log_likelihood == col.split(".")[0]]
+        elif isinstance(log_likelihood, dict):
+            log_likelihood = [
+                col
+                for col in columns
+                if any(item == col.split(".")[0] for item in log_likelihood.values())
+            ]
         else:
             log_likelihood = [
                 col for col in columns if any(item == col.split(".")[0] for item in log_likelihood)
@@ -430,13 +436,23 @@ class CmdStanConverter:
             data = _unpack_ndarrays(chain_data, columns, self.dtypes)
             data_warmup = _unpack_ndarrays(chain_data_warmup, columns, self.dtypes)
         else:
-            if isinstance(log_likelihood, str):
-                log_likelihood = [log_likelihood]
-            columns = {
-                col: idx
-                for col, idx in self.posterior_columns.items()
-                if any(item == col.split(".")[0] for item in log_likelihood)
-            }
+            if isinstance(log_likelihood, dict):
+                log_lik_to_obs_name = {v: k for k, v in log_likelihood.items()}
+                columns = {
+                    col.replace(col_name, log_lik_to_obs_name[col_name]): idx
+                    for col, col_name, idx in (
+                        (col, col.split(".")[0], idx) for col, idx in self.posterior_columns.items()
+                    )
+                    if any(item == col_name for item in log_likelihood.values())
+                }
+            else:
+                if isinstance(log_likelihood, str):
+                    log_likelihood = [log_likelihood]
+                columns = {
+                    col: idx
+                    for col, idx in self.posterior_columns.items()
+                    if any(item == col.split(".")[0] for item in log_likelihood)
+                }
             data = _unpack_ndarrays(self.posterior[0], columns, self.dtypes)
             data_warmup = _unpack_ndarrays(self.posterior[1], columns, self.dtypes)
             attrs = None
@@ -966,8 +982,14 @@ def from_cmdstan(
     predictions_constant_data_var : str or list of str, optional
         Variable(s) used for slicing predictions_constant_data.
         If not defined, all data variables are imported.
-    log_likelihood : str or list of str, optional
-        Pointwise log_likelihood for the data.
+    log_likelihood : dict of {str: str}, list of str or str, optional
+        Pointwise log_likelihood for the data. log_likelihood is extracted from the
+        posterior. It is recommended to use this argument as a dictionary whose keys
+        are observed variable names and its values are the variables storing log
+        likelihood arrays in the Stan code. In other cases, a dictionary with keys
+        equal to its values is used. By default, if a variable ``log_lik`` is
+        present in the Stan model, it will be retrieved as pointwise log
+        likelihood values. Use ``False`` to avoid this behaviour.
     index_origin : int, optional
         Starting value of integer coordinate values. Defaults to the value in rcParam
         ``data.index_origin``.
diff --git a/arviz/plots/loopitplot.py b/arviz/plots/loopitplot.py
index bd6d715..8f7a08f 100644
--- a/arviz/plots/loopitplot.py
+++ b/arviz/plots/loopitplot.py
@@ -53,7 +53,7 @@ def plot_loo_pit(
         (ECDF) and the uniform CDF instead of LOO-PIT kde.
         In this case, instead of overlaying uniform distributions, the beta ``hdi_prob``
         around the theoretical uniform CDF is shown. This approximation only holds
-        for large S and ECDF values not vary close to 0 nor 1. For more information, see
+        for large S and ECDF values not very close to 0 nor 1. For more information, see
         `Vehtari et al. (2019)`, `Appendix G <https://avehtari.github.io/rhat_ess/rhat_ess.html>`_.
     ecdf_fill : bool, optional
         Use :meth:`matplotlib.axes.Axes.fill_between` to mark the area
diff --git a/arviz/plots/traceplot.py b/arviz/plots/traceplot.py
index 56b9520..1e98b11 100644
--- a/arviz/plots/traceplot.py
+++ b/arviz/plots/traceplot.py
@@ -63,7 +63,7 @@ def plot_trace(
         Coordinates of var_names to be plotted. Passed to :meth:`xarray.Dataset.sel`
     divergences: {"bottom", "top", None}, optional
         Plot location of divergences on the traceplots.
-    kind: {"trace", "rank_bar", "rank_vlines"}, optional
+    kind: {"trace", "rank_bars", "rank_vlines"}, optional
         Choose between plotting sampled values per iteration and rank plots.
     transform: callable, optional
         Function to transform data (defaults to None i.e.the identity function)
diff --git a/arviz/py.typed b/arviz/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/arviz/stats/stats.py b/arviz/stats/stats.py
index a951051..d06cbca 100644
--- a/arviz/stats/stats.py
+++ b/arviz/stats/stats.py
@@ -239,17 +239,16 @@ def compare(
     p_ic = f"p_{ic}"
     ic_i = f"{ic}_i"
 
-    ics = pd.DataFrame()
-    names = []
+    ics_dict = {}
     for name, dataset in dataset_dict.items():
-        names.append(name)
         try:
             # Here is where the IC function is actually computed -- the rest of this
             # function is argument processing and return value formatting
-            ics = ics.append([ic_func(dataset, pointwise=True, scale=scale, var_name=var_name)])
+            ics_dict[name] = ic_func(dataset, pointwise=True, scale=scale, var_name=var_name)
         except Exception as e:
             raise e.__class__(f"Encountered error trying to compute {ic} from model {name}.") from e
-    ics.index = names
+
+    ics = pd.DataFrame.from_dict(ics_dict, orient="index")
     ics.sort_values(by=ic, inplace=True, ascending=ascending)
     ics[ic_i] = ics[ic_i].apply(lambda x: x.values.flatten())
 
@@ -1690,8 +1689,8 @@ def loo_pit(idata=None, *, y=None, y_hat=None, log_weights=None):
 
     if len(y.shape) + 1 != len(y_hat.shape):
         raise ValueError(
-            f"y_hat must have 1 more dimension than y, but y_hat has {len(y.shape)} dims and y has "
-            f"{len(y_hat.shape)} dims"
+            f"y_hat must have 1 more dimension than y, but y_hat has {len(y_hat.shape)} dims and "
+            f"y has {len(y.shape)} dims"
         )
 
     if y.shape != y_hat.shape[:-1]:
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 7f86156..93ca01e 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -6,7 +6,7 @@ pydocstyle
 pylint
 pytest
 pytest-cov
-black ; python_version >= '3.6'
+black==21.12b0 ; python_version >= '3.6'
 typing_copilot ; python_version >= '3.7'
 mypy<0.800
 cloudpickle<1.5.0


Test patch:
diff --git a/arviz/tests/external_tests/test_data_cmdstan.py b/arviz/tests/external_tests/test_data_cmdstan.py
index ac7334c..1ce6e95 100644
--- a/arviz/tests/external_tests/test_data_cmdstan.py
+++ b/arviz/tests/external_tests/test_data_cmdstan.py
@@ -299,6 +299,42 @@ class TestDataCmdStan:
             fails = check_multiple_attrs(test_dict, inference_data)
             assert not fails
 
+    def test_inference_data_input_types6(self, paths, observed_data_paths):
+        """Check input types (change, see earlier)
+
+        log_likelihood --> dict
+        """
+        for key, path in paths.items():
+            if "eight" not in key:
+                continue
+            post_pred = paths["eight_schools_glob"]
+            inference_data = self.get_inference_data(
+                posterior=path,
+                posterior_predictive=post_pred,
+                prior=path,
+                prior_predictive=post_pred,
+                observed_data=observed_data_paths[0],
+                observed_data_var=["y"],
+                log_likelihood={"y": "log_lik"},
+                coords={
+                    "school": np.arange(8),
+                    "log_lik_dim_0": np.arange(8),
+                    "y_hat": np.arange(8),
+                },
+                dims={"theta": ["school"], "y": ["school"], "y_hat": ["school"], "eta": ["school"]},
+            )
+            test_dict = {
+                "posterior": ["mu", "tau", "theta_tilde", "theta"],
+                "sample_stats": ["diverging"],
+                "prior": ["mu", "tau", "theta_tilde", "theta"],
+                "prior_predictive": ["y_hat"],
+                "observed_data": ["y"],
+                "posterior_predictive": ["y_hat"],
+                "log_likelihood": ["y", "~log_lik"],
+            }
+            fails = check_multiple_attrs(test_dict, inference_data)
+            assert not fails
+
     def test_inference_data_observed_data1(self, observed_data_paths):
         """Read Rdump/JSON, check shapes are correct