Skip to content

Commit

Permalink
Improve middle(::AbstractRange) performance (#116)
Browse files Browse the repository at this point in the history
* Restrict `middle(::AbstractRange)` eltype and improve performance

* Revert eltype restriction

* Apply suggestions from code review

Co-authored-by: Milan Bouchet-Valat <nalimilan@club.fr>

* Fix typo: `r` -> `a`

* Update test/runtests.jl

Co-authored-by: Milan Bouchet-Valat <nalimilan@club.fr>

* Fix CI failure

* Add `middle(0:typemax(Int))` test

Co-authored-by: Milan Bouchet-Valat <nalimilan@club.fr>
  • Loading branch information
vyu and nalimilan committed Jun 21, 2022
1 parent 576db0f commit 0588f2c
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 22 deletions.
29 changes: 11 additions & 18 deletions src/Statistics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -765,28 +765,16 @@ equivalent in both value and type to computing their mean (`(x + y) / 2`).
middle(x::Number, y::Number) = x/2 + y/2

"""
middle(range)
middle(a::AbstractArray)
Compute the middle of a range, which consists of computing the mean of its extrema.
Since a range is sorted, the mean is performed with the first and last element.
Compute the middle of an array `a`, which consists of finding its
extrema and then computing their mean.
```jldoctest
julia> using Statistics
julia> middle(1:10)
5.5
```
"""
middle(a::AbstractRange) = middle(a[1], a[end])

"""
middle(a)
Compute the middle of an array `a`, which consists of finding its
extrema and then computing their mean.
```jldoctest
julia> using Statistics
julia> a = [1,2,3.6,10.9]
4-element Vector{Float64}:
Expand All @@ -801,6 +789,11 @@ julia> middle(a)
"""
middle(a::AbstractArray) = ((v1, v2) = extrema(a); middle(v1, v2))

function middle(a::AbstractRange)
isempty(a) && throw(ArgumentError("middle of an empty range is undefined."))
return middle(first(a), last(a))
end

"""
median!(v)
Expand Down Expand Up @@ -997,9 +990,9 @@ end
require_one_based_indexing(v)

n = length(v)

@assert n > 0 # this case should never happen here

m = alpha + p * (one(alpha) - alpha - beta)
aleph = n*p + oftype(p, m)
j = clamp(trunc(Int, aleph), 1, n-1)
Expand All @@ -1012,7 +1005,7 @@ end
a = v[j]
b = v[j + 1]
end

if isfinite(a) && isfinite(b)
return a + γ*(b-a)
else
Expand Down
17 changes: 13 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ Random.seed!(123)
for T in [Bool,Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128,Float16,Float32,Float64]
@test middle(one(T)) === middle(one(T), one(T))
end

if VERSION < v"1.8.0-DEV.1343"
@test_throws ArgumentError middle(Int[])
else
@test_throws MethodError middle(Int[])
end
@test_throws ArgumentError middle(1:0)

@test middle(0:typemax(Int)) === typemax(Int) / 2
end

@testset "median" begin
Expand Down Expand Up @@ -547,16 +556,16 @@ end
@test cor(tmp, tmp) <= 1.0
@test cor(tmp, tmp2) <= 1.0
end

@test cor(Int[]) === 1.0
@test cor([im]) === 1.0 + 0.0im
@test_throws MethodError cor([])
@test_throws MethodError cor(Any[1.0])

@test cor([1, missing]) === 1.0
@test ismissing(cor([missing]))
@test_throws MethodError cor(Any[1.0, missing])

@test Statistics.corm([true], 1.0) === 1.0
@test_throws MethodError Statistics.corm(Any[0.0, 1.0], 0.5)
@test Statistics.corzm([true]) === 1.0
Expand Down Expand Up @@ -958,4 +967,4 @@ end
@test isequal(cor(mx, Int[]), fill(NaN, 2, 1))
@test isequal(cov(Int[], my), fill(-0.0, 1, 3))
@test isequal(cor(Int[], my), fill(NaN, 1, 3))
end
end

0 comments on commit 0588f2c

Please sign in to comment.