Skip to content

Commit

Permalink
Improve quantile in corner cases of collection eltype (#30938)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkamins authored and nalimilan committed Apr 1, 2019
1 parent a62b8ec commit cc516ab
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 21 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ Standard library changes

* Fixed `repr` such that it displays `DateTime` as it would be entered in Julia ([#30200]).

#### Statistics

* `quantile` now accepts in all cases collections whose `eltype` is not a subtype of `Number` ([#30938]).

#### Miscellaneous

* Since environment variables on Windows are case-insensitive, `ENV` now converts its keys
Expand Down
33 changes: 13 additions & 20 deletions stdlib/Statistics/src/Statistics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -853,19 +853,18 @@ function quantile!(q::AbstractArray, v::AbstractVector, p::AbstractArray;
return q
end

quantile!(v::AbstractVector, p::AbstractArray; sorted::Bool=false) =
quantile!(similar(p,float(eltype(v))), v, p; sorted=sorted)
function quantile!(v::AbstractVector, p::Union{AbstractArray, Tuple{Vararg{Real}}};
sorted::Bool=false)
if !isempty(p)
minp, maxp = extrema(p)
_quantilesort!(v, sorted, minp, maxp)
end
return map(x->_quantile(v, x), p)
end

quantile!(v::AbstractVector, p::Real; sorted::Bool=false) =
_quantile(_quantilesort!(v, sorted, p, p), p)

function quantile!(v::AbstractVector, p::Tuple{Vararg{Real}}; sorted::Bool=false)
isempty(p) && return ()
minp, maxp = extrema(p)
_quantilesort!(v, sorted, minp, maxp)
return map(x->_quantile(v, x), p)
end

# Function to perform partial sort of v for quantiles in given range
function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real)
isempty(v) && throw(ArgumentError("empty data vector"))
Expand Down Expand Up @@ -895,18 +894,12 @@ end
h = f0 - t0
i = trunc(Int,t0) + 1

T = promote_type(eltype(v), typeof(v[1]*h))

if h == 0
return convert(T, v[i])
a = v[i]
b = v[i + (h > 0)]
if isfinite(a) && isfinite(b)
return a + h*(b-a)
else
a = v[i]
b = v[i+1]
if isfinite(a) && isfinite(b)
return convert(T, a + h*(b-a))
else
return convert(T, (1-h)*a + h*b)
end
return (1-h)*a + h*b
end
end

Expand Down
35 changes: 34 additions & 1 deletion stdlib/Statistics/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,45 @@ end
@test quantile([0,1],1e-18) == 1e-18
@test quantile([1, 2, 3, 4],[]) == []
@test quantile([1, 2, 3, 4], (0.5,)) == (2.5,)
@test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], (0.1, 0.2, 0.4, 0.9)) == (2.0, 3.0, 5.0, 11.0)
@test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
(0.1, 0.2, 0.4, 0.9)) == (2.0, 3.0, 5.0, 11.0)
@test quantile(Union{Int, Missing}[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
[0.1, 0.2, 0.4, 0.9]) == [2.0, 3.0, 5.0, 11.0]
@test quantile(Any[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
[0.1, 0.2, 0.4, 0.9]) == [2.0, 3.0, 5.0, 11.0]
@test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
Any[0.1, 0.2, 0.4, 0.9]) == [2.0, 3.0, 5.0, 11.0]
@test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
Any[0.1, 0.2, 0.4, 0.9]) isa Vector{Float64}
@test quantile(Any[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
Any[0.1, 0.2, 0.4, 0.9]) == [2, 3, 5, 11]
@test quantile(Any[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11],
Any[0.1, 0.2, 0.4, 0.9]) isa Vector{Float64}
@test quantile([1, 2, 3, 4], ()) == ()
@test isempty(quantile([1, 2, 3, 4], Float64[]))
@test quantile([1, 2, 3, 4], Float64[]) isa Vector{Float64}
@test quantile([1, 2, 3, 4], []) isa Vector{Any}
@test quantile([1, 2, 3, 4], [0, 1]) isa Vector{Int}

@test quantile(Any[1, 2, 3], 0.5) isa Float64
@test quantile(Any[1, big(2), 3], 0.5) isa BigFloat
@test quantile(Any[1, 2, 3], Float16(0.5)) isa Float16
@test quantile(Any[1, Float16(2), 3], Float16(0.5)) isa Float16
@test quantile(Any[1, big(2), 3], Float16(0.5)) isa BigFloat

@test_throws ArgumentError quantile([1, missing], 0.5)
@test_throws ArgumentError quantile([1, NaN], 0.5)
@test quantile(skipmissing([1, missing, 2]), 0.5) === 1.5

# make sure that type inference works correctly in normal cases
for T in [Int, BigInt, Float64, Float16, BigFloat, Rational{Int}, Rational{BigInt}]
for S in [Float64, Float16, BigFloat, Rational{Int}, Rational{BigInt}]
@inferred quantile(T[1, 2, 3], S(0.5))
@inferred quantile(T[1, 2, 3], S(0.6))
@inferred quantile(T[1, 2, 3], S[0.5, 0.6])
@inferred quantile(T[1, 2, 3], (S(0.5), S(0.6)))
end
end
end

# StatsBase issue 164
Expand Down

0 comments on commit cc516ab

Please sign in to comment.