From 50910d0cffc5010e11ae9c1d10e383bbd0b00c44 Mon Sep 17 00:00:00 2001 From: Cornelius-G Date: Mon, 14 Dec 2020 16:29:53 +0100 Subject: [PATCH 1/4] merge --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3204c7f1b..4ad61cdcb 100644 --- a/Project.toml +++ b/Project.toml @@ -95,7 +95,6 @@ StatsBase = "0.32, 0.33" StructArrays = "0.4" Tables = "0.2, 1.0" TypedTables = "1.2" -ValueShapes = "0.7.3" julia = "1.3" [extras] From 98de319881c2f1b61a57b7d155ed128c6dc0977e Mon Sep 17 00:00:00 2001 From: Cornelius-G Date: Sat, 13 Mar 2021 12:20:46 +0100 Subject: [PATCH 2/4] add gaussian filtering for smoothing contour plots --- Project.toml | 4 +--- docs/src/plotting.md | 7 +++++-- examples/dev-internal/plotting_examples.jl | 8 ++++---- src/BAT.jl | 1 + src/algotypes/mode_estimator.jl | 2 +- src/plotting/recipes_MarginalDist_2D.jl | 8 +++++++- src/plotting/recipes_samples_1D.jl | 6 +++--- src/plotting/recipes_samples_2D.jl | 6 ++++-- test/plotting/test_localmodes.jl | 2 +- 9 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Project.toml b/Project.toml index 3219ef216..a7d311607 100644 --- a/Project.toml +++ b/Project.toml @@ -24,6 +24,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" +ImageFiltering = "6a3955dd-da59-5b1f-98d4-e7296123deb5" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" KernelDensity = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" @@ -95,10 +96,7 @@ StatsBase = "0.32, 0.33" StructArrays = "0.4, 0.5" Tables = "0.2, 1.0" TypedTables = "1.2" -<<<<<<< HEAD -======= ValueShapes = "0.8" ->>>>>>> upstream/master julia = "1.3" [extras] diff --git a/docs/src/plotting.md b/docs/src/plotting.md index ed09a37f9..ec9680cc1 100644 --- a/docs/src/plotting.md +++ b/docs/src/plotting.md @@ -49,7 +49,7 @@ The plot style of the mean can be customized using a `Dict`. For `mean = true`, `Dict("linestyle" => :dash, "linewidth" => 1, "linecolor" => :black, "alpha" => 1, "label" => "global mode")` * `marginalmode::Union{Dict, Bool} = true`: indicate the marginal mode(s), i.e. the center of the highest histogram bin(s) (currently only for samples). The style can be passed as a `Dict`. If `marginalmode = true`, the default style is: -`Dict("linestyle" => :dot, "linewidth" => 1, "linecolor" => :black, "alpha" => 1, "label" => "local mode")` +`Dict("linestyle" => :dot, "linewidth" => 1, "linecolor" => :black, "alpha" => 1, "label" => "marginal mode")` * (only for samples) `filter::Bool = false`: if `true`, `BAT.drop_low_weight_samples()` is applied before plotting @@ -72,6 +72,7 @@ plot( std = false, globalmode = false, marginalmode = true, + smoothing = 0, diagonal = Dict(), upper = Dict(), right = Dict(), @@ -111,7 +112,9 @@ The plot style of the mean can be customized using a `Dict`. For `mean = true`, `Dict("linestyle" => :dash, "linewidth" => 1, "linecolor" => :black, "alpha" => 1, "label" => "global mode")` * `marginalmode::Union{Dict, Bool} = true`: indicate the marginalmode(s), i.e. the center of the highest histogram bin(s) (currently only for samples). The style can be passed as a `Dict`. If `marginalmode = true`, the default style is: -`Dict("linestyle" => :dot, "linewidth" => 1, "linecolor" => :black, "alpha" => 1, "label" => "local mode")` +`Dict("linestyle" => :dot, "linewidth" => 1, "linecolor" => :black, "alpha" => 1, "label" => "marginal mode")` + +* `smoothing = 0`: When plotting contours, a Gaussian filtering can be applied for smoothing the contour lines. The keyword `smoothing` accepts positive real number (or a tuple of two positive real numbers), specifying the standard deviation of the Gaussian kernel (for each dimension) of the filtering. * `diagonal = Dict()`: Used only for the seriestype `:marginal`. The dictionary can contain the seriestypes and plot options for 2D distributions explained above to modify the 2D plot of the marginal plot. Nested Dictionaries are possible to modify the styles of the estimators as described above diff --git a/examples/dev-internal/plotting_examples.jl b/examples/dev-internal/plotting_examples.jl index 37a4d4327..7bbe7affa 100644 --- a/examples/dev-internal/plotting_examples.jl +++ b/examples/dev-internal/plotting_examples.jl @@ -58,7 +58,7 @@ using Plots plot(samples, :a) #default seriestype = :smallest_intervals (alias :HDR) #or: plot(samples, 2) -# The default seriestype for plotting samples is `:smallest_intervals` (alias `:HDR`), highlighting the smallest intervals (the highest density region) containing 68.3, 95.5 and 99.7 perecent of the posterior probability. By default, the local mode(s) of the histogram is(are) indicated as dotted black line(s). +# The default seriestype for plotting samples is `:smallest_intervals` (alias `:HDR`), highlighting the smallest intervals (the highest density region) containing 68.3, 95.5 and 99.7 perecent of the posterior probability. By default, the marginal mode(s) of the histogram is(are) indicated as dotted black line(s). # ### Default 1D plot of prior: # Priors can be plotted either by their index or by using the parameter name: @@ -118,7 +118,7 @@ plot(samples, :a, marginalmode=false, # ### Default 2D plot of samples: pyplot() plot(samples, (:a, :(b[1])), mean=true, std=true) #default seriestype = :smallest_intervals (alias :HDR) -# The default seriestype for plotting samples is a 3-color heatmap showing the smallest intervals (highest density regions) containing 68.3%, 95.5% and 99.7% of the posterior probability. By default, the local mode +# The default seriestype for plotting samples is a 3-color heatmap showing the smallest intervals (highest density regions) containing 68.3%, 95.5% and 99.7% of the posterior probability. By default, the marginal mode # of the histogram is indicated by a black square. # ### Default 2D plot of priors: @@ -136,9 +136,9 @@ plot(samples, (:a,:(b[2])), seriestype = :histogram) # (currently only correctly supported with `pyplot()` backend) plot(samples, (:a,:(b[2])), seriestype=:smallest_intervals_contour, bins=40) -# ### smallest intervals as filled contours: +# ### smallest intervals as filled contours with smoothing: # (currently only correctly supported with `pyplot()` backend) -plot(samples, (:a,:(b[2])), seriestype=:smallest_intervals_contourf, bins=40) +plot(samples, (:a,:(b[2])), seriestype=:smallest_intervals_contourf, bins=40, smoothing=1) # ### Customizing smallest interval plots: # The probability intervals to be highlighted can be specified using the `intervals` keyword. diff --git a/src/BAT.jl b/src/BAT.jl index d0578e6e7..2b59278f5 100644 --- a/src/BAT.jl +++ b/src/BAT.jl @@ -26,6 +26,7 @@ using DoubleFloats using ElasticArrays using FFTW using FillArrays +using ImageFiltering using IntervalSets using KernelDensity using LaTeXStrings diff --git a/src/algotypes/mode_estimator.jl b/src/algotypes/mode_estimator.jl index 7e86c72c7..112efd0bc 100644 --- a/src/algotypes/mode_estimator.jl +++ b/src/algotypes/mode_estimator.jl @@ -63,7 +63,7 @@ end *Experimental feature, not part of stable public API.* -Estimates a local mode of `samples` by finding the maximum of marginalized posterior for each dimension. +Estimates a marginal mode of `samples` by finding the maximum of marginalized posterior for each dimension. Returns a NamedTuple of the shape diff --git a/src/plotting/recipes_MarginalDist_2D.jl b/src/plotting/recipes_MarginalDist_2D.jl index 3ac9192e9..ae54e3e84 100644 --- a/src/plotting/recipes_MarginalDist_2D.jl +++ b/src/plotting/recipes_MarginalDist_2D.jl @@ -4,6 +4,7 @@ vsel::NTuple{2,Union{Symbol, Expr, Integer}}; intervals = default_credibilities, colors = default_colors, + smoothing = 0, diagonal = Dict(), upper = Dict(), right = Dict(), @@ -43,7 +44,12 @@ lev = calculate_levels(hist, intervals) x, y = get_bin_centers(marg) m = hist.weights - + + if smoothing != 0 + ker = ImageFiltering.Kernel.gaussian(smoothing) + m = imfilter(m, ker) + end + # quick fix: needed when plotting contour on top of histogram # otherwise scaling of histogram colorbar would change scaling lev = lev/10000 diff --git a/src/plotting/recipes_samples_1D.jl b/src/plotting/recipes_samples_1D.jl index 28aac3989..a359ccb96 100644 --- a/src/plotting/recipes_samples_1D.jl +++ b/src/plotting/recipes_samples_1D.jl @@ -118,7 +118,7 @@ end end - # local mode(s) + # marginal mode(s) if marginalmode_options != () marginalmode_values = find_marginalmodes(marg) @@ -126,9 +126,9 @@ @series begin seriestype := :line if length(marginalmode_values)==1 - label := get(marginalmode_options, "label", "local mode") + label := get(marginalmode_options, "label", "marginal mode") elseif i ==1 - label := get(marginalmode_options, "label", "local modes") + label := get(marginalmode_options, "label", "marginal modes") else label :="" end diff --git a/src/plotting/recipes_samples_2D.jl b/src/plotting/recipes_samples_2D.jl index 07b794ba6..b0a8cb22a 100644 --- a/src/plotting/recipes_samples_2D.jl +++ b/src/plotting/recipes_samples_2D.jl @@ -9,6 +9,7 @@ std = false, globalmode = false, marginalmode = true, + smoothing = 0, diagonal = Dict(), upper = Dict(), right = Dict(), @@ -92,6 +93,7 @@ diagonal --> diagonal upper --> upper right --> right + smoothing --> smoothing marg, (xindx, yindx) end @@ -168,9 +170,9 @@ @series begin seriestype := :scatter if i==1 && length(marginalmode_values)==1 - label := get(marginalmode_options, "label", "local mode") + label := get(marginalmode_options, "label", "marginal mode") elseif i ==1 - label := get(marginalmode_options, "label", "local modes") + label := get(marginalmode_options, "label", "marginal modes") else label :="" end diff --git a/test/plotting/test_localmodes.jl b/test/plotting/test_localmodes.jl index f6a070cf4..a2b15ddc1 100644 --- a/test/plotting/test_localmodes.jl +++ b/test/plotting/test_localmodes.jl @@ -5,7 +5,7 @@ using Plots using StatsBase -@testset "bin centers & local modes" begin +@testset "bin centers & marginal modes" begin data1 = [1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10] hist_1d = fit(Histogram, data1, nbins = 10, closed = :left) From 9db5d5832a7fef33274e7861e52c05a0ec087b4d Mon Sep 17 00:00:00 2001 From: Cornelius-G Date: Sat, 13 Mar 2021 12:52:48 +0100 Subject: [PATCH 3/4] change using to import ImageFiltering to prevent namespace problems --- src/BAT.jl | 2 +- src/plotting/recipes_MarginalDist_2D.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BAT.jl b/src/BAT.jl index 2b59278f5..f544ba6dc 100644 --- a/src/BAT.jl +++ b/src/BAT.jl @@ -26,7 +26,6 @@ using DoubleFloats using ElasticArrays using FFTW using FillArrays -using ImageFiltering using IntervalSets using KernelDensity using LaTeXStrings @@ -48,6 +47,7 @@ import DistributionsAD import EmpiricalDistributions import ForwardDiff import HypothesisTests +import ImageFiltering import Measurements import NamedArrays import NLSolversBase diff --git a/src/plotting/recipes_MarginalDist_2D.jl b/src/plotting/recipes_MarginalDist_2D.jl index ae54e3e84..099f9ae1f 100644 --- a/src/plotting/recipes_MarginalDist_2D.jl +++ b/src/plotting/recipes_MarginalDist_2D.jl @@ -47,7 +47,7 @@ if smoothing != 0 ker = ImageFiltering.Kernel.gaussian(smoothing) - m = imfilter(m, ker) + m = ImageFiltering.imfilter(m, ker) end # quick fix: needed when plotting contour on top of histogram From 7d47889eb88bdafa35c93e5ed5a0af653907c7cf Mon Sep 17 00:00:00 2001 From: Cornelius-G Date: Mon, 15 Mar 2021 21:30:16 +0100 Subject: [PATCH 4/4] remove dependency on ImageFiltering, add simple convolution implementation --- Project.toml | 1 - src/BAT.jl | 1 - src/plotting/recipes_MarginalDist_2D.jl | 4 +- src/utils/convolution_utils.jl | 61 +++++++++++++++++++++++++ src/utils/utils.jl | 1 + 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/utils/convolution_utils.jl diff --git a/Project.toml b/Project.toml index a7d311607..dfa0b1b58 100644 --- a/Project.toml +++ b/Project.toml @@ -24,7 +24,6 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" -ImageFiltering = "6a3955dd-da59-5b1f-98d4-e7296123deb5" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" KernelDensity = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" diff --git a/src/BAT.jl b/src/BAT.jl index f544ba6dc..d0578e6e7 100644 --- a/src/BAT.jl +++ b/src/BAT.jl @@ -47,7 +47,6 @@ import DistributionsAD import EmpiricalDistributions import ForwardDiff import HypothesisTests -import ImageFiltering import Measurements import NamedArrays import NLSolversBase diff --git a/src/plotting/recipes_MarginalDist_2D.jl b/src/plotting/recipes_MarginalDist_2D.jl index 099f9ae1f..04197bc0f 100644 --- a/src/plotting/recipes_MarginalDist_2D.jl +++ b/src/plotting/recipes_MarginalDist_2D.jl @@ -46,8 +46,8 @@ m = hist.weights if smoothing != 0 - ker = ImageFiltering.Kernel.gaussian(smoothing) - m = ImageFiltering.imfilter(m, ker) + ker = gaussian_kernel(smoothing) + m = convolution(m, ker, padding=:same) end # quick fix: needed when plotting contour on top of histogram diff --git a/src/utils/convolution_utils.jl b/src/utils/convolution_utils.jl new file mode 100644 index 000000000..59c4b6420 --- /dev/null +++ b/src/utils/convolution_utils.jl @@ -0,0 +1,61 @@ +# This file is a part of BAT.jl, licensed under the MIT License (MIT). + +# simple 2d convolution with padding +function convolution(input, filter; padding=:same) + input_r, input_c = size(input) + filter_r, filter_c = size(filter) + + if padding == :same + pad_r = (filter_r - 1) ÷ 2 + pad_c = (filter_c - 1) ÷ 2 + + input_padded = zeros(input_r+(2*pad_r), input_c+(2*pad_c)) + for i in 1:input_r, j in 1:input_c + input_padded[i+pad_r, j+pad_c] = input[i, j] + end + input = input_padded + input_r, input_c = size(input) + end + + result = zeros(input_r-filter_r+1, input_c-filter_c+1) + result_r, result_c = size(result) + + for i in 1:result_r + for j in 1:result_c + for k in 1:filter_r + for l in 1:filter_c + result[i,j] += input[i+k-1,j+l-1]*filter[k,l] + end + end + end + end + + return result +end + + +# gaussian kernel with same σ in both dimensions +function gaussian_kernel(σ::Real; l::Int = 4*ceil(Int,σ)+1) + isodd(l) || throw(ArgumentError("length must be odd")) + w = l>>1 + g = σ == 0 ? [exp(0/(2*oftype(σ, 1)^2))] : [exp(-x^2/(2*σ^2)) for x=-w:w] + k = g/sum(g) + return (k * k') +end + +# gaussian kernel with different σs in both dimensions +function gaussian_kernel( + σs::Tuple{Real, Real}; + l::Tuple{Int, Int} = (4*ceil(Int,σs[1])+1, 4*ceil(Int,σs[2])+1) +) + all(isodd.(l)) || throw(ArgumentError("length must be odd")) + w1 = l[1]>>1 + g1 = σs[1] == 0 ? [exp(0/(2*oftype(σs[1], 1)^2))] : [exp(-x^2/(2*σs[1]^2)) for x=-w1:w1] + k1 = g1/sum(g1) + + w2 = l[2]>>1 + g2 = σs[2] == 0 ? [exp(0/(2*oftype(σs[2], 1)^2))] : [exp(-x^2/(2*σs[2]^2)) for x=-w2:w2] + k2 = g2/sum(g2) + + return (k1 * k2') +end diff --git a/src/utils/utils.jl b/src/utils/utils.jl index d467a14fc..be2e41c6b 100644 --- a/src/utils/utils.jl +++ b/src/utils/utils.jl @@ -2,5 +2,6 @@ include("util_functions.jl") include("array_utils.jl") +include("convolution_utils.jl") include("coord_utils.jl") include("valueshapes_utils.jl")