Skip to content

AlgebraOfGraphics.jl

Documentation for AlgebraOfGraphics.jl ↗

AlgebraOfGraphics.jl (AoG) is a plotting package built on top of Makie, which provides a declarative interface for plotting, much like R's ggplot2.

FlexiChains does not provide any direct functionality to work with AoG. However, since FlexiChains provides a Tables.jl interface, it can be fed into AoG for plotting. This page provides a handful of examples.

We begin by sampling our familiar eight-schools model.

julia
using FlexiChains, Turing

y = [28, 8, -3, 7, -1, 1, 18, 12]
sigma = [15, 10, 16, 11, 9, 11, 10, 18]
@model function eight_schools(y, sigma)
    mu ~ Normal(0, 5)
    tau ~ truncated(Cauchy(0, 5); lower=0)
    theta ~ MvNormal(fill(mu, length(y)), tau^2 * I)
    for i in eachindex(y)
        y[i] ~ Normal(theta[i], sigma[i])
    end
    return (mu=mu, tau=tau)
end
model = eight_schools(y, sigma)
chain = sample(model, NUTS(), MCMCSerial(), 100, 3; progress=false)
╭─FlexiChain (100 iterations, 3 chains) ───────────────────────────────────────
 ↓ iter  = 51:150
 → chain = 1:3

 Parameters (3) ── VarName
  Float64          mu, tau                                                    
  Vector{Float64}  theta (8,)

 Extras (14)
  Int64    n_steps, tree_depth                                                
  Bool     is_accept, numerical_error                                         
  Float64  acceptance_rate, log_density, hamiltonian_energy,                  
           hamiltonian_energy_error, max_hamiltonian_energy_error, step_size, 
           nom_step_size, logprior, loglikelihood, logjoint                   
╰──────────────────────────────────────────────────────────────────────────────╯

Because AoG mainly works with long-form data, we need to specify this when creating a Tables.jl-compatible object, as the default for FlexiChains is a wide-form table. We also subset the chain to only include the theta parameters.

julia
using FlexiChains: Long

lng = Long(chain[[@varname(theta)]])

We can directly feed lng into AoG, without having to materialise it as a DataFrame. For the purposes of these docs we will take a look at (ten random rows of) the data:

julia
using DataFrames, Random

df = DataFrame(lng)
row_idxs = shuffle(1:nrow(df))[1:10]
df[row_idxs, :]
10×4 DataFrame
Rowiterchainparamvalue
Int64Int64VarName…Float64
1933theta[6]6.00331
21473theta[5]0.657534
3521theta[8]11.6736
41013theta[7]4.83158
5681theta[5]5.12162
6842theta[5]-0.968658
7652theta[8]3.36428
81483theta[7]4.74591
9791theta[6]-9.74546
10613theta[5]0.820062

Often when plotting you will want to facet by parameter. For a VNChain, it is necessary to apply the presorted transform, because VarNames do not have a natural ordering. The presorted function makes sure that the facets retain the order in the chain.

julia
using AlgebraOfGraphics, CairoMakie

d = data(lng)
m = mapping(:value, layout=:param => presorted)
v = AlgebraOfGraphics.density()

draw(d * m * v)

To avoid pooling all chains into a single plot, we can also split the chains into separate colours:

julia
d = data(lng)
m = mapping(:value, layout=:param => presorted, color=:chain => nonnumeric)
v = visual(Density; alpha=0.4)
draw(d * m * v)

And here is a violin plot:

julia
d = data(lng)
m = mapping(:param => presorted, :value)
v = visual(Violin; orientation=:horizontal)
draw(d * m * v)