Adding plotting functions for Makie
Overview
This PR is adding plotting functions to BAT for use with Makie.jl
Some basic plot types are implemented for 1d and 2d. The plotting is fully extensible by providing structs of abstract type plot_cfg_1d and plot_cfg_2d, respectively, together with corresponding function implementations:
function plot1d!(ax::Axis, cfg::plot_cfg_1d, x::AbstractArray, w::Union{Real, AbstractArray}=1)
...
end
or, respectively,
function plot2d!(ax::Axis, cfg::plot_cfg_2d, x::AbstractArray, y::AbstractArray, w::Union{Real, AbstractArray}=1)
...
end
Current recipes include:
1d plots:
- hist1d : simple density hist
- quantile_hist1d: density hist with colored quantiles
- kde1d: KDE
- quantile_kde1d: KDE with colored quantiles
- mean1d: lines for mean values
- std1d: lines for standard deviations
- pdf1d: priors
2d plots:
- hist2d: simple density hist
- quantile_hist2d: 2d quantiles based on a histogram
- kde2d: 2d KDE
- quantile_kde2d: 2d quantiles based on a KDE
- scatter2d: scatter plot
- mean2d: lines for mean values
- std2d: lines for standard deviations
- cov2d: ellipse of 2d covariance
- hexbin2d: hexbin density
Example usage:
using BAT
using MeasureBase
using DensityInterface
using Distributions
using LinearAlgebra
params = (a=1, b=1, c=1)
priors = (a=Normal(), b=Uniform(-2,2), c=Cauchy())
function fwd(a, b, c)
return MvNormal([a,b,c], I(3))
end
likelihood = likelihoodof(splat(fwd), [0,0,0])
prior = distprod(;priors...)
posterior = PosteriorMeasure(likelihood, prior)
samples = bat_sample(posterior).result
[ Info: Setting new default BAT context BATContext{Float64}(Random123.Philox4x{UInt64, 10}(0x0e7baf8ab291ff8c, 0x5b0485835223e643, 0x64f865a9c835b39b, 0xcbdf37fe9c162917, 0xdbf6235e76e2b0ed, 0xca60cc8ac70af658, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0), HeterogeneousComputing.CPUnit(), BAT._NoADSelected())
┌ Info: Using sampling algorithm TransformedMCMC{RandomWalk{TDist{Float64}}, NoMCMCProposalTuning, PriorToNormal, BAT.TriangularAffineTransform, RAMTuning, BAT.NoMCMCTempering, MCMCChainPoolInit, MCMCMultiCycleBurnin, BrooksGelmanConvergence, RepetitionWeighting{Int64}, typeof(BAT.nop_func)}
│ proposal: RandomWalk{TDist{Float64}}
│ proposal_tuning: NoMCMCProposalTuning NoMCMCProposalTuning()
│ pretransform: PriorToNormal PriorToNormal()
│ adaptive_transform: BAT.TriangularAffineTransform BAT.TriangularAffineTransform()
│ transform_tuning: RAMTuning
│ tempering: BAT.NoMCMCTempering BAT.NoMCMCTempering()
│ nchains: Int64 4
│ nwalkers: Int64 1
│ nsteps: Int64 100000
│ init: MCMCChainPoolInit
│ burnin: MCMCMultiCycleBurnin
│ convergence: BrooksGelmanConvergence
│ strict: Bool true
│ store_burnin: Bool false
│ nonzero_weights: Bool true
│ sample_weighting: RepetitionWeighting{Int64} RepetitionWeighting{Int64}()
└ callback: nop_func (function of type typeof(BAT.nop_func))
[ Info: MCMCChainPoolInit: trying to generate 4 viable MCMC chain state(s).
[ Info: Selected 4 MCMC chain state(s).
[ Info: Begin tuning of 4 MCMC chain(s).
[ Info: MCMC Tuning cycle 1 finished, 4 chains, 4 tuned, 4 converged.
[ Info: MCMC tuning of 4 chains successful after 1 cycle(s).
[ Info: Running post-tuning stabilization steps for 4 MCMC chain(s).
DensitySampleVector, StructArray with 5 columns and 93711 rows:
v logd weight info aux
┌──────────────────────────────────────────────────────────────────────────
1 │ (a = -0.527213, b = -… -8.00545 1 MCMCSampleID(2, 1, 5,… nothing
2 │ (a = -0.902307, b = 0… -8.81496 1 MCMCSampleID(2, 1, 5,… nothing
3 │ (a = -0.419528, b = -… -7.92013 5 MCMCSampleID(2, 1, 5,… nothing
4 │ (a = 1.27401, b = 0.4… -8.05164 7 MCMCSampleID(2, 1, 5,… nothing
5 │ (a = 0.828691, b = 1.… -7.77259 4 MCMCSampleID(2, 1, 5,… nothing
6 │ (a = -0.576574, b = 0… -7.18844 13 MCMCSampleID(2, 1, 5,… nothing
7 │ (a = -1.73847, b = 0.… -10.2505 12 MCMCSampleID(2, 1, 5,… nothing
8 │ (a = -1.44753, b = -0… -9.62168 1 MCMCSampleID(2, 1, 5,… nothing
9 │ (a = -1.18581, b = -0… -8.30775 4 MCMCSampleID(2, 1, 5,… nothing
10 │ (a = 0.272162, b = -0… -6.90886 4 MCMCSampleID(2, 1, 5,… nothing
11 │ (a = -0.600647, b = 0… -7.47888 2 MCMCSampleID(2, 1, 5,… nothing
12 │ (a = -0.17869, b = 0.… -6.71362 2 MCMCSampleID(2, 1, 5,… nothing
13 │ (a = -0.183446, b = 1… -6.8689 2 MCMCSampleID(2, 1, 5,… nothing
14 │ (a = -0.219895, b = 0… -6.36154 10 MCMCSampleID(2, 1, 5,… nothing
15 │ (a = -1.70216, b = 0.… -9.50329 2 MCMCSampleID(2, 1, 5,… nothing
16 │ (a = -2.36132, b = 0.… -11.9043 1 MCMCSampleID(2, 1, 5,… nothing
17 │ (a = -1.82908, b = 0.… -9.84521 2 MCMCSampleID(2, 1, 5,… nothing
18 │ (a = -0.703324, b = 0… -7.07286 6 MCMCSampleID(2, 1, 5,… nothing
19 │ (a = 1.28891, b = 0.6… -8.41924 16 MCMCSampleID(2, 1, 5,… nothing
20 │ (a = 1.00916, b = -0.… -7.56799 6 MCMCSampleID(2, 1, 5,… nothing
21 │ (a = 1.10518, b = 0.0… -7.75326 6 MCMCSampleID(2, 1, 5,… nothing
22 │ (a = -0.740396, b = 0… -7.06155 3 MCMCSampleID(2, 1, 5,… nothing
23 │ (a = -0.653221, b = -… -6.96074 2 MCMCSampleID(2, 1, 5,… nothing
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮
using CairoMakie, DataStructures
plot(samples)
variables = OrderedDict("a" => "Var A (units)", "b"=>"Var B", "c"=>"Var C")
OrderedDict{String, String} with 3 entries:
"a" => "Var A (units)"
"b" => "Var B"
"c" => "Var C"
fig = plot(samples, variables=variables, diagonal=quantile_kde1d(), lower_triangle=nothing, upper_triangle=scatter2d(size=0.5))
fig = plot!(fig, samples, variables=variables, lower_triangle=quantile_kde2d())
fig = plot!(fig, samples, variables=variables, lower_triangle=mean2d(color=:orange, linestyle=:solid), upper_triangle=mean2d(color=:black, linestyle=:solid, linewidth=1))
fig = plot!(fig, samples, variables=variables, upper_triangle=cov2d(linewidth=1.5))
fig = plot!(fig, samples, variables=variables, diagonal=mean1d(color=:orange, linestyle=:solid))
fig = plot!(fig, samples, variables=variables, diagonal=std1d(color=:orange, linestyle=:dash), lower_triangle=std2d(color=:orange, linestyle=:dash))
fig = plot!(fig, prior, variables=variables, diagonal=pdf1d(edgecolor=:magenta, color=:magenta, edge=true, filled=true, edgewidth=1, alpha=0.2))
Codecov Report
Attention: Patch coverage is 0% with 264 lines in your changes missing coverage. Please review.
Project coverage is 48.20%. Comparing base (
b08cff2) to head (6e54e1d).
| Files with missing lines | Patch % | Lines |
|---|---|---|
| src/plotting/plotting_makie.jl | 0.00% | 264 Missing :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## main #477 +/- ##
==========================================
- Coverage 50.34% 48.20% -2.14%
==========================================
Files 121 122 +1
Lines 5961 6225 +264
==========================================
Hits 3001 3001
- Misses 2960 3224 +264
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
ping @oschulz
Thanks for the reminder, I'll take a look later!
@oschulz ?
I talked to some of the Makie developers at JuliaCon. They strongly discourage specializing Makie.plot and Makie.plot! to implement type-speficic plotting recipes. Instead, we're supposed to specialize Makie.convert_arguments and use Makie.PlotSpec when subplots are required (kinda like in https://github.com/JuliaHEP/HEPExampleProject.jl/blob/main/ext/HEPExampleProjectMakieExt.jl).