ArviZ Quickstart

%matplotlib inline
import arviz as az
import numpy as np

# ArviZ ships with style sheets!'arviz-darkgrid')

Get started with plotting

ArviZ is designed to be used with libraries like PyStan and PyMC3, but works fine with raw numpy arrays.


Plotting a dictionary of arrays, ArviZ will interpret each key as the name of a different random variable. Each row of an array is treated as an independent series of draws from the variable, called a chain. Below, we have 10 chains of 50 draws each for four different distributions.

size = (10, 50)
    'normal': np.random.randn(*size),
    'gumbel': np.random.gumbel(size=size),
    'student t': np.random.standard_t(df=6, size=size),
    'exponential': np.random.exponential(size=size)

Plotting with PyMC3 objects

ArviZ is designed to work well with high dimensional, labelled data. Consider the eight schools model, which roughly tries to measure the effectiveness of SAT classes at eight different schools. To show off ArviZ’s labelling, I give the schools the names of a different eight schools.

This model is small enough to write down, is hierarchical, uses labelling, and a centered parameterization causes divergences (which are interesting for illustration):

import pymc3 as pm

J = 8
y = np.array([28.,  8., -3.,  7., -1.,  1., 18., 12.])
sigma = np.array([15., 10., 16., 11.,  9., 11., 10., 18.])
schools = np.array(['Choate', 'Deerfield', 'Phillips Andover', 'Phillips Exeter',
                    'Hotchkiss', 'Lawrenceville', "St. Paul's", 'Mt. Hermon'])

with pm.Model() as centered_eight:
    mu = pm.Normal('mu', mu=0, sd=5)
    tau = pm.HalfCauchy('tau', beta=5)
    theta = pm.Normal('theta', mu=mu, sd=tau, shape=J)
    obs = pm.Normal('obs', mu=theta, sd=sigma, observed=y)

    # This pattern is useful in PyMC3
    prior = pm.sample_prior_predictive()
    centered_eight_trace = pm.sample()
    posterior_predictive = pm.sample_posterior_predictive(centered_eight_trace)
Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [theta, tau, mu]
Sampling 4 chains, 36 divergences: 100%|██████████| 4000/4000 [00:02<00:00, 1817.41draws/s]
There were 15 divergences after tuning. Increase `target_accept` or reparameterize.
The acceptance probability does not match the target. It is 0.6078223532452149, but should be close to 0.8. Try to increase the number of tuning steps.
There were 4 divergences after tuning. Increase `target_accept` or reparameterize.
There were 4 divergences after tuning. Increase `target_accept` or reparameterize.
There were 13 divergences after tuning. Increase `target_accept` or reparameterize.
The rhat statistic is larger than 1.05 for some parameters. This indicates slight problems during sampling.
The estimated number of effective samples is smaller than 200 for some parameters.
100%|██████████| 2000/2000 [00:00<00:00, 3175.64it/s]

Most ArviZ functions work fine with trace objects from PyMC3:

az.plot_autocorr(centered_eight_trace, var_names=['mu', 'tau']);

Convert to InferenceData

For much more powerful querying, analysis and plotting, we can use built-in ArviZ utilities to convert PyMC3 objects to xarray datasets. Note we are also giving some information about labelling.

ArviZ is built to work with InferenceData, and the more groups it has access to, the more powerful analyses it can perform. See the InferenceData structure specification here. Here is a plot of the trace, which is common in PyMC3 workflows. Note the intelligent labels.

data = az.from_pymc3(trace=centered_eight_trace,
                     coords={'school': schools},
                     dims={'theta': ['school'], 'obs': ['school']})
Inference data with groups:
        > posterior
        > sample_stats
        > log_likelihood
        > posterior_predictive
        > prior
        > prior_predictive
        > observed_data

Plotting with PyStan objects

ArviZ is built with first class support for PyStan objects, and can plot raw fit objects in a reasonable manner. Here is the same centered eight schools model:

import pystan

schools_code = """
data {
  int<lower=0> J;
  real y[J];
  real<lower=0> sigma[J];

parameters {
  real mu;
  real<lower=0> tau;
  real theta[J];

model {
  mu ~ normal(0, 5);
  tau ~ cauchy(0, 5);
  theta ~ normal(mu, tau);
  y ~ normal(theta, sigma);
generated quantities {
    vector[J] log_lik;
    vector[J] y_hat;
    for (j in 1:J) {
        log_lik[j] = normal_lpdf(y[j] | theta[j], sigma[j]);
        y_hat[j] = normal_rng(theta[j], sigma[j]);

schools_dat = {'J': 8,
               'y': [28,  8, -3,  7, -1,  1, 18, 12],
               'sigma': [15, 10, 16, 11,  9, 11, 10, 18]}

sm = pystan.StanModel(model_code=schools_code, verbose=False)
fit = sm.sampling(data=schools_dat, iter=1000, chains=4)
INFO:pystan:COMPILING THE C++ CODE FOR MODEL anon_model_b0eada194ad938e30ed47926216f98ca NOW.
WARNING:pystan:35 of 2000 iterations ended with a divergence (1.75 %).
WARNING:pystan:Try running with adapt_delta larger than 0.8 to remove the divergences.
az.plot_density(fit, var_names=['mu', 'tau']);

Again, converting to InferenceData (a netcdf datastore that loads data into xarray datasets), we can get much richer labelling and mixing of data. Here is a plot showing where the Hamiltonian sampler had divergences

data = az.from_pystan(posterior=fit,
                      log_likelihood={'y': 'log_lik'},
                      coords={'school': schools},
                      dims={'theta': ['school'], 'y': ['school'], 'log_lik': ['school'], 'y_hat': ['school'], 'theta_tilde': ['school']})
Inference data with groups:
        > posterior
        > sample_stats
        > log_likelihood
        > posterior_predictive
        > observed_data
az.plot_pair(data, coords={'school': ['Choate', 'Deerfield', 'Phillips Andover']}, divergences=True);