From 89ba1cb00dd2c5175557e58d35e20fee55e3d861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Wed, 8 Sep 2021 10:29:32 +0200 Subject: [PATCH 1/4] Improve quicksort performance --- src/Statistics.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Statistics.jl b/src/Statistics.jl index 29aae101..00c4cc51 100644 --- a/src/Statistics.jl +++ b/src/Statistics.jl @@ -938,7 +938,7 @@ function quantile!(q::AbstractArray, v::AbstractVector, p::AbstractArray; isempty(q) && return q minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp) + _quantilesort!(v, sorted, minp, maxp, length(p)) for (i, j) in zip(eachindex(p), eachindex(q)) @inbounds q[j] = _quantile(v,p[i], alpha=alpha, beta=beta) @@ -950,16 +950,16 @@ function quantile!(v::AbstractVector, p::Union{AbstractArray, Tuple{Vararg{Real} sorted::Bool=false, alpha::Real=1., beta::Real=alpha) if !isempty(p) minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp) + _quantilesort!(v, sorted, minp, maxp, length(p)) end return map(x->_quantile(v, x, alpha=alpha, beta=beta), p) end quantile!(v::AbstractVector, p::Real; sorted::Bool=false, alpha::Real=1., beta::Real=alpha) = - _quantile(_quantilesort!(v, sorted, p, p), p, alpha=alpha, beta=beta) + _quantile(_quantilesort!(v, sorted, p, p, 1), p, alpha=alpha, beta=beta) # Function to perform partial sort of v for quantiles in given range -function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real) +function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real, len::Int) isempty(v) && throw(ArgumentError("empty data vector")) require_one_based_indexing(v) @@ -967,9 +967,13 @@ function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real) lv = length(v) lo = floor(Int,minp*(lv)) hi = ceil(Int,1+maxp*(lv)) - # only need to perform partial sort - sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) + if len == 2 + sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:lo), Base.Sort.Forward) + sort!(v, 1, lv, Base.Sort.PartialQuickSort(hi:hi), Base.Sort.Forward) + else + sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) + end end if (sorted && (ismissing(v[end]) || (v[end] isa Number && isnan(v[end])))) || any(x -> ismissing(x) || (x isa Number && isnan(x)), v) From 5b8e75d429673dc9143ebd2a7d1e841139eb92d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Wed, 8 Sep 2021 10:32:26 +0200 Subject: [PATCH 2/4] Update Statistics.jl --- src/Statistics.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Statistics.jl b/src/Statistics.jl index 00c4cc51..6ac392e8 100644 --- a/src/Statistics.jl +++ b/src/Statistics.jl @@ -965,13 +965,17 @@ function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real, if !sorted lv = length(v) - lo = floor(Int,minp*(lv)) - hi = ceil(Int,1+maxp*(lv)) # only need to perform partial sort if len == 2 - sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:lo), Base.Sort.Forward) - sort!(v, 1, lv, Base.Sort.PartialQuickSort(hi:hi), Base.Sort.Forward) + lo1 = floor(Int,minp*(lv)) + lo2 = ceil(Int,1+minp*(lv)) + sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo1:lo2), Base.Sort.Forward) + hi1 = floor(Int,maxp*(lv)) + hi2 = ceil(Int,1+maxp*(lv)) + sort!(v, 1, lv, Base.Sort.PartialQuickSort(hi1:hi2), Base.Sort.Forward) else + lo = floor(Int,minp*(lv)) + hi = ceil(Int,1+maxp*(lv)) sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) end end From adaf1111ab5622cbdf1c24cb4b524a112728f575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Wed, 8 Sep 2021 11:04:33 +0200 Subject: [PATCH 3/4] Apply suggestions from code review --- src/Statistics.jl | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Statistics.jl b/src/Statistics.jl index 6ac392e8..252137f5 100644 --- a/src/Statistics.jl +++ b/src/Statistics.jl @@ -938,7 +938,7 @@ function quantile!(q::AbstractArray, v::AbstractVector, p::AbstractArray; isempty(q) && return q minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp, length(p)) + _quantilesort!(v, sorted, minp, maxp) for (i, j) in zip(eachindex(p), eachindex(q)) @inbounds q[j] = _quantile(v,p[i], alpha=alpha, beta=beta) @@ -950,34 +950,27 @@ function quantile!(v::AbstractVector, p::Union{AbstractArray, Tuple{Vararg{Real} sorted::Bool=false, alpha::Real=1., beta::Real=alpha) if !isempty(p) minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp, length(p)) + _quantilesort!(v, sorted, minp, maxp) end return map(x->_quantile(v, x, alpha=alpha, beta=beta), p) end quantile!(v::AbstractVector, p::Real; sorted::Bool=false, alpha::Real=1., beta::Real=alpha) = - _quantile(_quantilesort!(v, sorted, p, p, 1), p, alpha=alpha, beta=beta) + _quantile(_quantilesort!(v, sorted, p, p), p, alpha=alpha, beta=beta) # Function to perform partial sort of v for quantiles in given range -function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real, len::Int) +function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real) isempty(v) && throw(ArgumentError("empty data vector")) require_one_based_indexing(v) if !sorted lv = length(v) # only need to perform partial sort - if len == 2 - lo1 = floor(Int,minp*(lv)) - lo2 = ceil(Int,1+minp*(lv)) - sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo1:lo2), Base.Sort.Forward) - hi1 = floor(Int,maxp*(lv)) - hi2 = ceil(Int,1+maxp*(lv)) - sort!(v, 1, lv, Base.Sort.PartialQuickSort(hi1:hi2), Base.Sort.Forward) - else - lo = floor(Int,minp*(lv)) - hi = ceil(Int,1+maxp*(lv)) - sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) - end + lo = floor(Int,minp*(lv)) + hi = ceil(Int,1+maxp*(lv)) + + # only need to perform partial sort + sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) end end if (sorted && (ismissing(v[end]) || (v[end] isa Number && isnan(v[end])))) || any(x -> ismissing(x) || (x isa Number && isnan(x)), v) From f8015c88b90f7aaf89bae53df90fcc7171f40480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Wed, 8 Sep 2021 11:14:11 +0200 Subject: [PATCH 4/4] Update Statistics.jl --- src/Statistics.jl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Statistics.jl b/src/Statistics.jl index 252137f5..8a8701d6 100644 --- a/src/Statistics.jl +++ b/src/Statistics.jl @@ -937,22 +937,33 @@ function quantile!(q::AbstractArray, v::AbstractVector, p::AbstractArray; end isempty(q) && return q - minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp) + if length(p) == 2 + for (i, j) in zip(eachindex(p), eachindex(q)) + @inbounds q[j] = quantile!(v, p[i], sorted=sorted, alpha=alpha, beta=beta) + end + else + minp, maxp = extrema(p) + _quantilesort!(v, sorted, minp, maxp) - for (i, j) in zip(eachindex(p), eachindex(q)) - @inbounds q[j] = _quantile(v,p[i], alpha=alpha, beta=beta) + for (i, j) in zip(eachindex(p), eachindex(q)) + @inbounds q[j] = _quantile(v, p[i], alpha=alpha, beta=beta) + end end + return q end function quantile!(v::AbstractVector, p::Union{AbstractArray, Tuple{Vararg{Real}}}; sorted::Bool=false, alpha::Real=1., beta::Real=alpha) + if length(p) == 2 + return map(x -> quantile!(v, x, sorted=sorted, alpha=alpha, beta=beta), p) + end + if !isempty(p) minp, maxp = extrema(p) _quantilesort!(v, sorted, minp, maxp) end - return map(x->_quantile(v, x, alpha=alpha, beta=beta), p) + return map(x -> _quantile(v, x, alpha=alpha, beta=beta), p) end quantile!(v::AbstractVector, p::Real; sorted::Bool=false, alpha::Real=1., beta::Real=alpha) = @@ -969,8 +980,7 @@ function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real) lo = floor(Int,minp*(lv)) hi = ceil(Int,1+maxp*(lv)) - # only need to perform partial sort - sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) end + sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) end if (sorted && (ismissing(v[end]) || (v[end] isa Number && isnan(v[end])))) || any(x -> ismissing(x) || (x isa Number && isnan(x)), v)