Skip to content

Commit a0b7417

Browse files
rofinnararslan
andcommitted
Add exponential weights
Co-authored-by: Alex Arslan <[email protected]>
1 parent 66b0a16 commit a0b7417

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

docs/src/weights.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ w = ProbabilityWeights([0.2, 0.1, 0.3])
4141
w = pweights([0.2, 0.1, 0.3])
4242
```
4343

44+
### `ExponentialWeights`
45+
46+
Exponential weights are a common form of temporal weights which assign exponentially decreasing
47+
weight to past observations.
48+
49+
```julia
50+
w = ExponentialWeights([0.1837, 0.2222, 0.2688, 0.3253])
51+
w = eweights(4, 0.173) # construction based on length and rate parameter
52+
```
53+
4454
### `Weights`
4555

4656
The `Weights` type describes a generic weights vector which does not support all operations possible for `FrequencyWeights`, `AnalyticWeights` and `ProbabilityWeights`.
@@ -66,9 +76,11 @@ The following constructors are provided:
6676
AnalyticWeights
6777
FrequencyWeights
6878
ProbabilityWeights
79+
ExponentialWeights
6980
Weights
7081
aweights
7182
fweights
7283
pweights
84+
eweights
7385
weights
74-
```
86+
```

src/StatsBase.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ module StatsBase
2929
AnalyticWeights, # to represent an analytic/precision/reliability weight vector
3030
FrequencyWeights, # to representing a frequency/case/repeat weight vector
3131
ProbabilityWeights, # to representing a probability/sampling weight vector
32+
ExponentialWeights, # to represent an exponential weight vector
3233
weights, # construct a generic Weights vector
3334
aweights, # construct an AnalyticWeights vector
3435
fweights, # construct a FrequencyWeights vector
3536
pweights, # construct a ProbabilityWeights vector
37+
eweights, # construct an ExponentialWeights vector
3638
wsum, # weighted sum with vector as second argument
3739
wsum!, # weighted sum across dimensions with provided storage
3840
wmean, # weighted mean

src/weights.jl

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,59 @@ pweights(vs::RealArray) = ProbabilityWeights(vec(vs))
193193
end
194194
end
195195

196+
@weights ExponentialWeights
197+
198+
@doc """
199+
ExponentialWeights(vs, wsum=sum(vs))
200+
201+
Construct an `ExponentialWeights` vector with weight values `vs`.
202+
A precomputed sum may be provided as `wsum`.
203+
204+
Exponential weights are a common form of temporal weights which assign exponentially
205+
decreasing weight to past observations, which in this case corresponds to the front of
206+
the vector. That is, newer observations are assumed to be at the end.
207+
""" ExponentialWeights
208+
209+
"""
210+
eweights(n, λ)
211+
212+
Construct an [`ExponentialWeights`](@ref) vector with length `n`,
213+
where each element in position ``i`` is set to ``λ (1 - λ)^{1 - i}``.
214+
215+
``λ`` is a smoothing factor or rate parameter such that ``0 < λ \\leq 1``.
216+
As this value approaches 0, the resulting weights will be almost equal,
217+
while values closer to 1 will put greater weight on the tail elements of the vector.
218+
219+
# Examples
220+
221+
```julia-repl
222+
julia> eweights(10, 0.3)
223+
10-element ExponentialWeights{Float64,Float64,Array{Float64,1}}:
224+
0.3
225+
0.42857142857142855
226+
0.6122448979591837
227+
0.8746355685131197
228+
1.249479383590171
229+
1.7849705479859588
230+
2.549957925694227
231+
3.642797036706039
232+
5.203995766722913
233+
7.434279666747019
234+
```
235+
"""
236+
function eweights(n::Integer, λ::Real)
237+
n > 0 || throw(ArgumentError("cannot construct exponential weights of length < 1"))
238+
0 < λ <= 1 || throw(ArgumentError("smoothing factor must be between 0 and 1"))
239+
w0 = map(i -> λ * (1 - λ)^(1 - i), 1:n)
240+
s = sum(w0)
241+
ExponentialWeights{typeof(s), eltype(w0), typeof(w0)}(w0, s)
242+
end
243+
244+
# NOTE: No variance correction is implemented for exponential weights
245+
196246
##### Equality tests #####
197247

198-
for w in (AnalyticWeights, FrequencyWeights, ProbabilityWeights, Weights)
248+
for w in (AnalyticWeights, FrequencyWeights, ProbabilityWeights, ExponentialWeights, Weights)
199249
@eval begin
200250
Base.isequal(x::$w, y::$w) = isequal(x.sum, y.sum) && isequal(x.values, y.values)
201251
Base.:(==)(x::$w, y::$w) = (x.sum == y.sum) && (x.values == y.values)

test/weights.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ using StatsBase
22
using LinearAlgebra, Random, SparseArrays, Test
33

44
@testset "StatsBase.Weights" begin
5+
# NOTE: Do not add eweights here, as its methods don't match those of the others, so the
6+
# tests below don't make sense for it
57
weight_funcs = (weights, aweights, fweights, pweights)
68

79
# Construction
@@ -497,4 +499,22 @@ end
497499
@test wquantile(data[1], w, 0.5) answer atol = 1e-5
498500
end
499501

502+
@testset "ExponentialWeights" begin
503+
@testset "Basic Usage" begin
504+
θ = 5.25
505+
λ = 1 - exp(-1 / θ) # simple conversion for the more common/readable method
506+
507+
v =*(1-λ)^(1-i) for i = 1:4]
508+
w = ExponentialWeights(v)
509+
510+
@test round.(w, digits=4) == [0.1734, 0.2098, 0.2539, 0.3071]
511+
@test eweights(4, λ) w
512+
end
513+
514+
@testset "Failure Conditions" begin
515+
@test_throws ArgumentError eweights(0, 0.3)
516+
@test_throws ArgumentError eweights(1, 1.1)
517+
end
518+
end
519+
500520
end # @testset StatsBase.Weights

0 commit comments

Comments
 (0)