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.
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.
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:
using DataFrames, Random
df = DataFrame(lng)
row_idxs = shuffle(1:nrow(df))[1:10]
df[row_idxs, :]| Row | iter | chain | param | value |
|---|---|---|---|---|
| Int64 | Int64 | VarName… | Float64 | |
| 1 | 93 | 3 | theta[6] | 6.00331 |
| 2 | 147 | 3 | theta[5] | 0.657534 |
| 3 | 52 | 1 | theta[8] | 11.6736 |
| 4 | 101 | 3 | theta[7] | 4.83158 |
| 5 | 68 | 1 | theta[5] | 5.12162 |
| 6 | 84 | 2 | theta[5] | -0.968658 |
| 7 | 65 | 2 | theta[8] | 3.36428 |
| 8 | 148 | 3 | theta[7] | 4.74591 |
| 9 | 79 | 1 | theta[6] | -9.74546 |
| 10 | 61 | 3 | theta[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.
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:
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:
d = data(lng)
m = mapping(:param => presorted, :value)
v = visual(Violin; orientation=:horizontal)
draw(d * m * v)