Skip to content

Commit

Permalink
Merge pull request #113 from legend-exp/singlefits
Browse files Browse the repository at this point in the history
Bugfix in `singlefits.jl`
  • Loading branch information
fhagemann authored Feb 17, 2025
2 parents 497dadb + f458bb7 commit efa8962
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 16 deletions.
2 changes: 1 addition & 1 deletion ext/LegendSpecFitsRecipesBaseExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ end
xlims := (ustrip(Measurements.value(report.μ - 5*report.σ)), ustrip(Measurements.value(report.μ + 5*report.σ)))
yscale --> :identity
yticks := ([-3, 0, 3])
collect(report.h.edges[1])[1:end-1] .+ diff(collect(report.h.edges[1]))[1]/2 , [ifelse(abs(r) < 1e-6, 0.0, r) for r in report.gof.residuals_norm]
report.gof.bin_centers, [ifelse(abs(r) < 1e-6, 0.0, r) for r in report.gof.residuals_norm]
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion src/gof.jl
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ Calcualte bin-wise p-value based on poisson distribution for each bin.
* `residuals` difference: model - data (histogram bin count)
* `residuals_norm` normalized residuals: model - data / sqrt(model)
* `p_value_binwise` p-value for each bin based on poisson distribution
* `bin_centers` centers of the bins for which the `residuals` were determined
"""
function get_residuals(f_fit::Base.Callable, h::Histogram{<:Real,1}, v_ml::Union{NamedTuple, AbstractVector})
# prepare data
Expand All @@ -187,6 +188,6 @@ function get_residuals(f_fit::Base.Callable, h::Histogram{<:Real,1}, v_ml::Union
cdf_value_low = cdf.(dist, model_counts[model_counts .> 0] .- abs.(residuals))
cdf_value_up = 1 .- cdf.(dist, model_counts[model_counts .> 0] .+ abs.(residuals))
p_value_binwise = cdf_value_low .+ cdf_value_up # significance of residuals -> ~proabability that residual (for a given bin) is as large as observed or larger
return residuals, residuals_norm, p_value_binwise, bin_centers
return residuals, residuals_norm, p_value_binwise, bin_centers[model_counts .> 0]
end

33 changes: 19 additions & 14 deletions src/singlefit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ Returns `report` and `result`` with:
* `n`: number of counts in the peak
"""
function fit_single_trunc_gauss(x::Vector{<:Unitful.RealOrRealQuantity}, cuts::NamedTuple{(:low, :high, :max), Tuple{<:T, <:T, <:T}}=(low = zero(first(x))*NaN, high = zero(first(x))*NaN, max = zero(first(x))*NaN); uncertainty::Bool=true) where T<:Unitful.RealOrRealQuantity
@assert unit(cuts.low) == unit(cuts.high) == unit(cuts.max) == unit(x[1]) "Units of min_x, max_x and x must be the same"
x_unit = unit(x[1])
x, cut_low, cut_high, cut_max = ustrip.(x), ustrip(cuts.low), ustrip(cuts.high), ustrip(cuts.max)
@assert unit(cuts.low) == unit(cuts.high) == unit(cuts.max) == unit(first(x)) "Units of min_x, max_x and x must be the same"
x_unit = unit(first(x))
x, cut_low, cut_high, cut_max = ustrip.(x_unit, x), ustrip(x_unit, cuts.low), ustrip(x_unit, cuts.high), ustrip(x_unit, cuts.max)
cut_low, cut_high = ifelse(isnan(cut_low), minimum(x), cut_low), ifelse(isnan(cut_high), maximum(x), cut_high)

bin_width = get_friedman_diaconis_bin_width(x[(x .> cut_low) .&& (x .< cut_high)])
x_min, x_max = minimum(x), maximum(x)
bin_width = get_friedman_diaconis_bin_width(x[cut_low .<= x .<= cut_high])
x_min, x_max = extrema(x)
x_nocut = copy(x)
h_nocut = fit(Histogram, x, x_min:bin_width:x_max)
ps = estimate_single_peak_stats_simple(h_nocut)
@debug "Peak stats: $ps"

# cut peak out of data
x = x[(x .> cut_low) .&& (x .< cut_high)]
x = x[cut_low .<= x .<= cut_high]
h = fit(Histogram, x, cut_low:bin_width:cut_high)
n = length(x)

Expand Down Expand Up @@ -104,9 +104,11 @@ function fit_single_trunc_gauss(x::Vector{<:Unitful.RealOrRealQuantity}, cuts::N
end

# create histogram of nocut data for normalization 20 sigma around peak
h_nocut = fit(Histogram, x_nocut, v_ml.μ - 20*v_ml.σ:bin_width:v_ml.μ + 20*v_ml.σ)
# ensuring that the bin edges are identical to those of h (on which the fit was performed)
h_nocut = fit(Histogram, x_nocut, (cut_low + floor(((v_ml.μ-20*v_ml.σ) - cut_low) / bin_width) * bin_width):bin_width:(cut_low + ceil(((v_ml.μ+20*v_ml.σ) - cut_low) / bin_width) * bin_width))

# normalize nocut histogram to PDF of cut histogram
h_pdf = Histogram(h_nocut.edges[1], h_nocut.weights ./ sum(abs.(h.weights)) ./ step(h.edges[1]))
h_pdf = Histogram(first(h_nocut.edges), h_nocut.weights ./ sum(abs.(h.weights)) ./ step(first(h.edges)))

report = (
f_fit = t -> Base.Fix2(f_fit, v_ml)(t),
Expand All @@ -133,15 +135,15 @@ function fit_half_centered_trunc_gauss(x::Vector{<:Unitful.RealOrRealQuantity},
x, cut_low, cut_high, cut_max, μ = ustrip.(x), ustrip(cuts.low), ustrip(cuts.high), ustrip(cuts.max), ustrip(μ)

# get peak stats
bin_width = get_friedman_diaconis_bin_width(x[(x .> cut_low) .&& (x .< cut_high)])
bin_width = get_friedman_diaconis_bin_width(x[cut_low .<= x .<= cut_high])
x_min, x_max = minimum(x), maximum(x)
x_nocut = copy(x)
h_nocut = fit(Histogram, x, x_min:bin_width:x_max)
ps = estimate_single_peak_stats_simple(h_nocut)
@debug "Peak stats: $ps"

# cut peak out of data
x = ifelse(left, x[(x .> cut_low) .&& (x .< cut_high) .&& x .< μ], x[(x .> cut_low) .&& (x .< cut_high) .&& x .> μ])
x = x[cut_low .<= x .<= cut_high .&& ifelse(left, x .<= μ, x .>= μ)]
h = fit(Histogram, x, ifelse(left, cut_low, μ):bin_width:ifelse(left, μ, cut_high))
n = length(x)

Expand Down Expand Up @@ -221,7 +223,9 @@ function fit_half_centered_trunc_gauss(x::Vector{<:Unitful.RealOrRealQuantity},
end

# create histogram of nocut data for normalization 20 sigma around peak
h_nocut = fit(Histogram, x_nocut, v_ml.μ - 20*v_ml.σ:bin_width:v_ml.μ + 20*v_ml.σ)
# ensuring that the bin edges are identical to those of h (on which the fit was performed)
h_nocut = fit(Histogram, x_nocut, (ifelse(left, cut_low, μ) + floor(((v_ml.μ-20*v_ml.σ) - ifelse(left, cut_low, μ)) / bin_width) * bin_width):bin_width:(ifelse(left, cut_low, μ) + ceil(((v_ml.μ+20*v_ml.σ) - ifelse(left, cut_low, μ)) / bin_width) * bin_width))

# normalize nocut histogram to PDF of cut histogram
h_pdf = Histogram(h_nocut.edges[1], h_nocut.weights ./ sum(abs.(h.weights)) ./ step(h.edges[1]))

Expand Down Expand Up @@ -252,15 +256,15 @@ function fit_half_trunc_gauss(x::Vector{<:Unitful.RealOrRealQuantity}, cuts::Nam
x, cut_low, cut_high, cut_max = ustrip.(x), ustrip(cuts.low), ustrip(cuts.high), ustrip(cuts.max)

# get peak stats
bin_width = get_friedman_diaconis_bin_width(x[(x .> cut_low) .&& (x .< cut_high)])
bin_width = get_friedman_diaconis_bin_width(x[cut_low .<= x .<= cut_high])
x_min, x_max = minimum(x), maximum(x)
x_nocut = copy(x)
h_nocut = fit(Histogram, x, x_min:bin_width:x_max)
ps = estimate_single_peak_stats_simple(h_nocut)
@debug "Peak stats: $ps"

# cut peak out of data
x = x[(x .> ifelse(left, cut_low, cut_max)) .&& (x .< ifelse(left, cut_max, cut_high))]
x = x[ifelse(left, cut_low, cut_max) .<= x .<= ifelse(left, cut_max, cut_high)]
h = fit(Histogram, x, ifelse(left, cut_low, cut_max):bin_width:ifelse(left, cut_max, cut_high))
n = length(x)

Expand Down Expand Up @@ -340,7 +344,8 @@ function fit_half_trunc_gauss(x::Vector{<:Unitful.RealOrRealQuantity}, cuts::Nam
end

# create histogram of nocut data for normalization 20 sigma around peak
h_nocut = fit(Histogram, x_nocut, v_ml.μ - 20*v_ml.σ:bin_width:v_ml.μ + 20*v_ml.σ)
h_nocut = fit(Histogram, x_nocut, (ifelse(left, cut_low, cut_max) + floor(((v_ml.μ-20*v_ml.σ) - ifelse(left, cut_low, cut_max)) / bin_width) * bin_width):bin_width:(ifelse(left, cut_low, cut_max) + ceil(((v_ml.μ+20*v_ml.σ) - ifelse(left, cut_low, cut_max)) / bin_width) * bin_width))

# normalize nocut histogram to PDF of cut histogram
h_pdf = Histogram(h_nocut.edges[1], h_nocut.weights ./ sum(abs.(h.weights)) ./ step(h.edges[1]))

Expand Down

0 comments on commit efa8962

Please sign in to comment.