Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions src/Statistics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,18 @@ end
# faster computation of real(conj(x)*y)
realXcY(x::Real, y::Real) = x*y
realXcY(x::Complex, y::Complex) = real(x)*real(y) + imag(x)*imag(y)
realXcY(x::Number, y::Number) = real(conj(x) * y)

var(iterable; corrected::Bool=true, mean=nothing) = _var(iterable, corrected, mean)

function _var(iterable, corrected::Bool, mean)
y = iterate(iterable)
if y === nothing
T = eltype(iterable)
# When `zero` cannot be called we throw an error
if T === Union{} || !(T <: Number)
throw(ArgumentError("cannot compute variance of an empty iterator with eltype $T"))
end
return oftype((abs2(zero(T)) + abs2(zero(T)))/2, NaN)
end
count = 1
Expand Down Expand Up @@ -398,29 +403,25 @@ varm(iterable, m; corrected::Bool=true) = _var(iterable, corrected, m)

## variances over ranges

varm(v::AbstractRange, m::AbstractArray) = range_varm(v, m)
varm(v::AbstractRange, m) = range_varm(v, m)
function varm(v::AbstractRange, m; corrected::Bool=true)
l = length(v)
l <= 1 && return _var(v, corrected, m)
vv = if m === nothing
s = step(v)
abs2(s) * (l + 1) * l / 12
else
range_varm(v, m)
end
return corrected ? vv : vv * (l - 1) / l
end

function range_varm(v::AbstractRange, m)
f = first(v) - m
s = step(v)
l = length(v)
vv = f^2 * l / (l - 1) + f * s * l + s^2 * l * (2 * l - 1) / 6
if l == 0 || l == 1
return typeof(vv)(NaN)
end
return vv
return abs2(f) * l / (l - 1) + realXcY(f, s) * l + abs2(s) * l * (2 * l - 1) / 6
end

function var(v::AbstractRange)
s = step(v)
l = length(v)
vv = abs2(s) * (l + 1) * l / 12
if l == 0 || l == 1
return typeof(vv)(NaN)
end
return vv
end


##### standard deviation #####
Expand Down
37 changes: 32 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,10 @@ end
@testset "var & std" begin
# edge case: empty vector
# iterable; this has to throw for type stability
@test_throws Exception var(())
@test_throws Exception var((); corrected=false)
@test_throws Exception var((); mean=2)
@test_throws Exception var((); mean=2, corrected=false)
@test_throws ArgumentError var(())
@test_throws ArgumentError var((); corrected=false)
@test_throws ArgumentError var((); mean=2)
@test_throws ArgumentError var((); mean=2, corrected=false)
# reduction
@test isnan(var(Int[]))
@test isnan(var(Int[]; corrected=false))
Expand Down Expand Up @@ -278,10 +278,29 @@ end
@test var([1], dims=1; mean=[2], corrected=false) ≈ [1.0]

@test var(1:8) == 6.
@test varm(1:8,1) == varm(Vector(1:8),1)
@test varm(1:8, 1) == varm(Vector(1:8), 1)
@test var(1:8; mean=1) == var(Vector(1:8); mean=1)
@test var(1:8; corrected=false) == var(Vector(1:8); corrected=false)
@test varm(1:8, 1; corrected=false) == varm(Vector(1:8), 1; corrected=false)
@test var(1:8; mean=1, corrected=false) == var(Vector(1:8); mean=1, corrected=false)
@test isnan(varm(1:1,1))
@test isnan(var(1:1))
@test isnan(var(1:1; mean=1))
@test !isnan(var(1:1; corrected=false))
@test isnan(var(1:0; corrected=false))
@test !isnan(varm(1:1, 1; corrected=false))
@test isnan(varm(1:0, 1; corrected=false))
@test isnan(var(1:-1))
Comment thread
MilesCranmer marked this conversation as resolved.
@test isnan(varm(1:-1,1))
@test isnan(var(1:-1; mean=1))
@test isnan(var(1:-1; corrected=false))
@test isnan(varm(1:-1, 1; corrected=false))

let r = LinRange(1 + 2im, 3 + 4im, 11), m = mean(r)
@test (@inferred var(r))::Float64 ≈ var(r; mean=m)
@test (@inferred var(r; mean=m))::Float64 ≈ var(r)
@test (@inferred varm(r, m))::Float64 ≈ varm(collect(r), m)
end

@test @inferred(var(1.0:8.0)) == 6.
@test varm(1.0:8.0,1.0) == varm(Vector(1.0:8.0),1)
Expand Down Expand Up @@ -922,6 +941,14 @@ Statistics.middle(x::Furlong{p}, y::Furlong{p}) where {p} = Furlong{p}(middle(x.
@test var(r) == var(a) == Furlong{2}(0.5)
@test std(r) == std(a) == Furlong{1}(sqrt(0.5))

@test var(r; corrected=false) == var(a; corrected=false)

m = Furlong(100)
@test var(r; mean=m) == var(a; mean=m)
@test var(r; mean=m, corrected=false) == var(a; mean=m, corrected=false)
@test std(r; mean=m) == std(a; mean=m)
@test std(r; mean=m, corrected=false) == std(a; mean=m, corrected=false)

# Issue #21786
A = [Furlong{1}(rand(-5:5)) for i in 1:2, j in 1:2]
@test mean(mean(A, dims=1), dims=2)[1] === mean(A)
Expand Down