BAT.jl icon indicating copy to clipboard operation
BAT.jl copied to clipboard

Adding plotting functions for Makie

Open philippeller opened this issue 7 months ago • 5 comments

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)

download

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))

download

philippeller avatar May 27 '25 13:05 philippeller

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.

codecov[bot] avatar May 27 '25 14:05 codecov[bot]

ping @oschulz

philippeller avatar Jul 07 '25 06:07 philippeller

Thanks for the reminder, I'll take a look later!

oschulz avatar Jul 07 '25 06:07 oschulz

@oschulz ?

philippeller avatar Sep 09 '25 06:09 philippeller

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).

oschulz avatar Sep 09 '25 09:09 oschulz