diff --git a/Project.toml b/Project.toml index 85cad26..4313d4a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FHist" uuid = "68837c9b-b678-4cd5-9925-8a54edc8f695" authors = ["Moelf ", "Nick Amin "] -version = "0.10.8" +version = "0.10.9" [deps] BayesHistogram = "000d9b38-65fe-4c81-bdb9-69f01f102479" diff --git a/src/hist1d.jl b/src/hist1d.jl index 32021b2..fbfaac1 100644 --- a/src/hist1d.jl +++ b/src/hist1d.jl @@ -309,7 +309,10 @@ Merges `n` consecutive bins into one. The returned histogram will have `nbins(h)/n` bins. """ function rebin(h::Hist1D, n::Int=1) - @assert nbins(h) % n == 0 + if nbins(h) % n != 0 + rebin_values = join(sort(collect(valid_rebin_values(h))), ", ", " or ") + error("Invalid rebin value ($n) for a 1D histogram with $(nbins(h)) bins. It has to be a multiple of $(rebin_values)") + end p = x->Iterators.partition(x, n) counts = sum.(p(bincounts(h))) sumw2 = sum.(p(h.sumw2)) diff --git a/src/hist2d.jl b/src/hist2d.jl index abe50bc..65100b6 100644 --- a/src/hist2d.jl +++ b/src/hist2d.jl @@ -238,7 +238,10 @@ Merges `nx` (`ny`) consecutive bins into one along the x (y) axis by summing. """ function rebin(h::Hist2D, nx::Int=1, ny::Int=nx) sx, sy = nbins(h) - @assert sx % nx == sy % ny == 0 + if !(sx % nx == sy % ny == 0) + rebin_values_x, rebin_values_y = map(x->join(sort(collect(x)), ", ", " or "), valid_rebin_values(h)) + error("Invalid rebin values (nx: $nx, ny: $ny) for a 2D histogram with $(nbins(h)) bins. They have to be a multple of nx: $(rebin_values_x) and ny: $(rebin_values_y)") + end p1d = (x,n)->Iterators.partition(x, n) p2d = x->(x[i:i+(nx-1),j:j+(ny-1)] for i=1:nx:sx, j=1:ny:sy) counts = sum.(p2d(bincounts(h))) @@ -251,6 +254,7 @@ function rebin(h::Hist2D, nx::Int=1, ny::Int=nx) end rebin(nx::Int, ny::Int) = h::Hist2D -> rebin(h, nx, ny) + """ project(h::Hist2D, axis::Symbol=:x) project(axis::Symbol=:x) = h::Hist2D -> project(h, axis) diff --git a/src/utils.jl b/src/utils.jl index ffbbc68..2beb231 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -35,3 +35,33 @@ end function _is_uniform_bins(A::AbstractRange{T}) where T<:Real true end + +""" + valid_rebin_values(h::Union{Hist1D, Hist2D, Hist3D}) + +Calculates the legal values for rebinning, essentially the prime factors of +the number of bins. For a 1D histogram, a `Set` of numbers is return, for higher +dimensional histograms a `Vector{Set}` for each dimension. +""" +valid_rebin_values(h::Hist1D) = _factor(nbins(h)) +valid_rebin_values(h::Union{Hist2D, Hist3D}) = [_factor(x) for x in nbins(h)] + +""" + function _factor(n::Integer) + +Helper function to calculate the prime factors of a given integer. +""" +function _factor(n::Integer) + factors = Set{Int}() + limit = n + factor = 2 + while n > 1 & factor < limit + while n % factor == 0 + n /= factor + push!(factors, factor) + limit = sqrt(n) + end + factor += 1 + end + factors +end diff --git a/test/runtests.jl b/test/runtests.jl index dca3aec..9be498f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -419,6 +419,7 @@ end @test integral(h1) == integral(rebin(h1, 5)) @test sum(h1.sumw2) == sum(rebin(h1, 5).sumw2) @test binedges(rebin(h1, 5)) == [0, 0.5, 1.0] + @test Set([2, 5]) == FHist.valid_rebin_values(h1) h2 = Hist1D(rand(10^2), [0.0, 0.1, 0.7, 0.9, 1.0]) @test h2 == rebin(h2, 1) @@ -426,6 +427,7 @@ end @test nentries(h2) == nentries(rebin(h2, 1)) @test sum(h2.sumw2) == sum(rebin(h2, 2).sumw2) @test binedges(rebin(h2, 2)) == [0, 0.7, 1.0] + @test Set([2]) == FHist.valid_rebin_values(h2) @test rebin(h1, 2) == (h1 |> rebin(2)) @@ -434,6 +436,7 @@ end @test integral(h1) == integral(rebin(h1, 5)) @test sum(h1.sumw2) == sum(rebin(h1, 5).sumw2) @test binedges(rebin(h1, 5)) == ([0, 0.5, 1.0], [0, 0.5, 1.0]) + @test Set([2]) == FHist.valid_rebin_values(h2) bins = [0.0, 0.1, 0.7, 0.9, 1.0] h2 = Hist2D((rand(10^2),rand(10^2)), (bins,bins)) @@ -441,10 +444,12 @@ end @test integral(h2) == integral(rebin(h2, 2)) @test sum(h2.sumw2) == sum(rebin(h2, 2).sumw2) @test binedges(rebin(h2, 2)) == ([0, 0.7, 1.0], [0, 0.7, 1.0]) + @test [Set([2]), Set([2])] == FHist.valid_rebin_values(h2) h2 = Hist2D((rand(10^2),rand(10^2)), (0:0.1:1,0:0.5:1)) @test nbins(rebin(h2, 10, 2)) == (1, 1) - @test_throws AssertionError rebin(h2, 2, 10) + @test_throws ErrorException rebin(h2, 2, 10) + @test [Set([5, 2]), Set([2])] == FHist.valid_rebin_values(h2) end @testset "Profile" begin