Skip to content

Commit 210c1cc

Browse files
committed
Support mixed units/types of coordinates and axes
1 parent a7e9e67 commit 210c1cc

File tree

2 files changed

+29
-21
lines changed

2 files changed

+29
-21
lines changed

src/histogram.jl

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct HistEdge{T,I}
2626
end
2727
HistEdge(edge::AbstractRange) = HistEdge(first(edge), last(edge), length(edge) - 1)
2828

29+
Base.eltype(::HistEdge{T}) where {T} = T
30+
2931

3032
function lookup(edge::HistEdge{T,I}, x::T) where {T,I}
3133
U = _unitless(T)
@@ -51,10 +53,10 @@ end
5153

5254
function _hist_inner!(::HistogramBinning,
5355
dest::AbstractArray{R,N},
54-
edges::NTuple{N,HistEdge{T,I}},
55-
coord::NTuple{N,T},
56-
weight::U
57-
) where {R, N, T, I, U}
56+
edges::Tuple{Vararg{HistEdge,N}},
57+
coord::Tuple{Vararg{Any,N}},
58+
weight::W
59+
) where {R, N, W}
5860
idx = map(ii -> lookup(edges[ii], coord[ii])[1], ntuple(identity, Val(N)))
5961
dest[idx...] += oneunit(R) * weight
6062
return nothing
@@ -64,16 +66,16 @@ end
6466

6567
function _hist_inner!(::LinearBinning,
6668
dest::AbstractArray{R,N},
67-
edges::NTuple{N,HistEdge{T,I}},
68-
coord::NTuple{N,T},
69-
weight::U
70-
) where {R, N, T, I, U}
69+
edges::Tuple{Vararg{HistEdge,N}},
70+
coord::Tuple{Vararg{Any,N}},
71+
weight::W
72+
) where {R, N, W}
7173
Z = ntuple(identity, Val(N))
7274
len = map(ii -> edges[ii].nbin, Z)
7375
idx = map(ii -> lookup(edges[ii], coord[ii])[1], Z)
7476
# subtract off half of bin step to convert from fraction from left edge to fraction
7577
# away from center
76-
del = map(ii -> lookup(edges[ii], coord[ii])[2] - one(U) / 2, Z)
78+
del = map(ii -> lookup(edges[ii], coord[ii])[2] - one(W) / 2, Z)
7779

7880
# iterate through all corners of a bounding hypercube by counting counting through
7981
# the permutations of {0,1} for each "axis" mapped to a bit in an integer
@@ -88,7 +90,7 @@ function _hist_inner!(::LinearBinning,
8890
# the fraction of the weight to assign to a particular corner of the hypercube is
8991
# the volume of the intersection between the bin and a similarly-shaped cube around
9092
# the original coordinate
91-
frac = mapreduce(i -> bits[i] ? abs(del[i]) : one(U) - abs(del[i]), *, Z)
93+
frac = mapreduce(i -> bits[i] ? abs(del[i]) : one(del[i]) - abs(del[i]), *, Z)
9294

9395
dest[idxs...] += oneunit(R) * weight * frac
9496
end
@@ -97,20 +99,20 @@ end
9799

98100
function _histogram!(binning::B,
99101
dest::AbstractArray{R,N},
100-
edges::NTuple{N,HistEdge{T,I}},
101-
data::AbstractVector{<:NTuple{N,T}},
102+
edges::Tuple{Vararg{HistEdge,N}},
103+
data::AbstractVector{<:Tuple{Vararg{Any,N}}},
102104
weights::Union{Nothing,<:AbstractVector},
103-
) where {B<:AbstractBinning, R, N, T, I}
105+
) where {B<:AbstractBinning, R, N}
104106
Z = ntuple(identity, Val(N))
105107

106108
# run through data vector and bin entries if they are within bounds
107-
wsum = isnothing(weights) ? zero(_unitless(T)) : zero(eltype(weights))
109+
wsum = isnothing(weights) ? zero(_unitless(R)) : zero(eltype(weights))
108110
for ii in eachindex(data)
109111
coord = @inbounds data[ii]
110112
if !mapreduce(i -> edges[i].lo coord[i] edges[i].hi, &, Z)
111113
continue
112114
end
113-
w = isnothing(weights) ? one(_unitless(T)) : weights[ii]
115+
w = isnothing(weights) ? one(_unitless(R)) : weights[ii]
114116
_hist_inner!(binning, dest, edges, coord, w)
115117
wsum += w
116118
end
@@ -129,4 +131,8 @@ function _histogram!(binning::B,
129131
return wsum
130132
end
131133

134+
__hist_eltype(eltypes) = _invunit(typeof(mapreduce(oneunit, *, eltypes)))
135+
_hist_eltype(edges::Tuple{Vararg{AbstractRange}}) = __hist_eltype(map(eltype, edges))
136+
_hist_eltype(edges::Tuple{Vararg{HistEdge}}) = __hist_eltype(map(eltype, edges))
137+
132138
end # module Histogramming

test/histogram.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,16 @@ end
118118
end
119119

120120
@testset "Unitful numbers" begin
121-
# given an n-tuple value with units,
122-
vals = [x .* u"m" for x in x1]
123-
# the histogram axes have the same units, and the density has inverse units
124-
uedges = map(c -> c .* u"m", edges)
125-
uhist = zeros(typeof(1.0u"m^-1"^N), axes(hist)...)
121+
using .Histogramming: _hist_eltype
122+
123+
# make data unitful, with mixed unit axes
124+
units = (u"m", u"s^-2", u"kg")[1:N]
125+
vals = [x .* units for x in x1]
126+
uedges = map((e, u) -> e .* u, edges, units)
127+
uhist = zeros(_hist_eltype(uedges), axes(hist)...)
126128
# verify that the function accepts unitful quantities
127129
@test _histogram!(style, uhist, HistEdge.(uedges), vals, nothing) === 1.0
128-
@test sum(uhist) * step(uedges[1])^N 1.0 rtol=2eps(1.0)
130+
@test sum(uhist) * mapreduce(step, *, uedges) 1.0 rtol=2eps(1.0)
129131
end
130132
end
131133

0 commit comments

Comments
 (0)