Skip to content

Commit 7f5a722

Browse files
Merge pull request #121 from juliangehring/faster-sort
Improve sorting in p-values adjustment methods
2 parents c5d8091 + e68bc09 commit 7f5a722

File tree

5 files changed

+33
-32
lines changed

5 files changed

+33
-32
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Changes
66

7+
- Improve the reordering strategy of p-values in adjustment methods. This change saves one sorting step for all adjustment methods that require sorted p-values. As a result, the performance for these methods is significantly improved.
78
- Pin `Documenter` to v0.19 for building of the documentation (#104)
89

910

src/pval-adjustment.jl

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,11 @@ function adjust(pValues::PValues{T}, n::Integer, method::BenjaminiHochberg) wher
153153
if k <= 1
154154
return pValues
155155
end
156-
sortedOrder, originalOrder = reorder(pValues)
156+
sortedOrder = sortperm(pValues)
157157
pAdjusted = pValues[sortedOrder]
158158
pAdjusted .*= n ./ (1:k)
159159
stepup!(pAdjusted)
160-
pAdjusted = clamp.(pAdjusted[originalOrder], 0, 1)
160+
pAdjusted[sortedOrder] = clamp.(pAdjusted, 0, 1)
161161
return pAdjusted
162162
end
163163

@@ -262,11 +262,11 @@ function adjust(pValues::PValues{T}, n::Integer, method::BenjaminiYekutieli) whe
262262
if k <= 1
263263
return pValues
264264
end
265-
sortedOrder, originalOrder = reorder(pValues)
265+
sortedOrder = sortperm(pValues)
266266
pAdjusted = pValues[sortedOrder]
267267
pAdjusted .*= harmonic_number(n) .* n ./ (1:k)
268268
stepup!(pAdjusted)
269-
pAdjusted = clamp.(pAdjusted[originalOrder], 0, 1)
269+
pAdjusted[sortedOrder] = clamp.(pAdjusted, 0, 1)
270270
return pAdjusted
271271
end
272272

@@ -315,14 +315,14 @@ function adjust(pValues::PValues{T}, n::Integer, method::BenjaminiLiu) where T <
315315
if n <= 1
316316
return pValues
317317
end
318-
sortedOrder, originalOrder = reorder(pValues)
318+
sortedOrder = sortperm(pValues)
319319
pAdjusted = pValues[sortedOrder]
320320
# a bit more involved because cutoffs at significance α have the form:
321321
# P_(i) <= 1- [1 - min(1, m/(m-i+1)α)]^{1/(m-i+1)}
322322
s = n .- (1:k) .+ 1
323323
pAdjusted = (1 .- (1 .- pAdjusted).^s) .* s ./ n
324324
stepdown!(pAdjusted)
325-
pAdjusted = clamp.(pAdjusted[originalOrder], 0, 1)
325+
pAdjusted[sortedOrder] = clamp.(pAdjusted, 0, 1)
326326
return pAdjusted
327327
end
328328

@@ -370,11 +370,11 @@ function adjust(pValues::PValues{T}, n::Integer, method::Hochberg) where T <: Ab
370370
if k <= 1
371371
return pValues
372372
end
373-
sortedOrder, originalOrder = reorder(pValues)
373+
sortedOrder = sortperm(pValues)
374374
pAdjusted = pValues[sortedOrder]
375375
pAdjusted .*= (n .- (1:k) .+ 1)
376376
stepup!(pAdjusted)
377-
pAdjusted = clamp.(pAdjusted[originalOrder], 0, 1)
377+
pAdjusted[sortedOrder] = clamp.(pAdjusted, 0, 1)
378378
return pAdjusted
379379
end
380380

@@ -422,11 +422,11 @@ function adjust(pValues::PValues{T}, n::Integer, method::Holm) where T <: Abstra
422422
if n <= 1
423423
return pValues
424424
end
425-
sortedOrder, originalOrder = reorder(pValues)
425+
sortedOrder = sortperm(pValues)
426426
pAdjusted = pValues[sortedOrder]
427427
pAdjusted .*= (n .- (1:k) .+ 1)
428428
stepdown!(pAdjusted)
429-
pAdjusted = clamp.(pAdjusted[originalOrder], 0, 1)
429+
pAdjusted[sortedOrder] = clamp.(pAdjusted, 0, 1)
430430
return pAdjusted
431431
end
432432

@@ -474,7 +474,7 @@ function adjust(pValues::PValues{T}, n::Integer, method::Hommel) where T <: Abst
474474
if k <= 1
475475
return pValues
476476
end
477-
sortedOrder, originalOrder = reorder(pValues)
477+
sortedOrder = sortperm(pValues)
478478
pAdjusted = vcat(pValues[sortedOrder], fill(one(T), n - k))
479479
lower = n * minimum(pAdjusted ./ (1:n))
480480
q = fill(lower, n)
@@ -487,7 +487,9 @@ function adjust(pValues::PValues{T}, n::Integer, method::Hommel) where T <: Abst
487487
q[idx_right] .= q[n - j + 1]
488488
pa .= max.(pa, q)
489489
end
490-
pAdjusted = max.(pa[originalOrder], pValues)
490+
pa = pa[1:k]
491+
pa[sortedOrder] = pa
492+
pAdjusted = max.(pa, pValues)
491493
return pAdjusted
492494
end
493495

@@ -578,12 +580,12 @@ adjust(pValues::PValues{T}, method::ForwardStop) where T <: AbstractFloat = adju
578580
function adjust(pValues::PValues{T}, n::Integer, method::ForwardStop) where T <: AbstractFloat
579581
k = length(pValues)
580582
check_number_tests(k, n)
581-
sortedOrder, originalOrder = reorder(pValues)
583+
sortedOrder = sortperm(pValues)
582584
logsums = -cumsum(log.(1 .- pValues[sortedOrder]))
583585
logsums ./= (1:k)
584586
stepup!(logsums)
585-
pAdjusted = clamp.(logsums[originalOrder], 0, 1)
586-
return pAdjusted
587+
logsums[sortedOrder] = clamp.(logsums, 0, 1)
588+
return logsums
587589
end
588590

589591

@@ -628,7 +630,7 @@ function adjust(pValues::PValues{T}, method::BarberCandes) where T <: AbstractFl
628630
return fill(1 / n, size(pValues))
629631
end
630632

631-
sorted_indexes, original_order = reorder(pValues)
633+
sorted_indexes = sortperm(pValues)
632634
estimated_fdrs = pValues[sorted_indexes]
633635

634636
Rt = 1 # current number of discoveries
@@ -655,7 +657,8 @@ function adjust(pValues::PValues{T}, method::BarberCandes) where T <: AbstractFl
655657
end
656658

657659
stepup!(estimated_fdrs)
658-
pAdjusted = clamp.(estimated_fdrs[original_order], 0, 1)
660+
pAdjusted = clamp.(estimated_fdrs, 0, 1)
661+
pAdjusted[sorted_indexes] = pAdjusted
659662
return pAdjusted
660663
end
661664

src/utils.jl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
## utility functions ##
22

3-
function reorder(values::AbstractVector{T}) where T <: Real
4-
newOrder = sortperm(values)
5-
oldOrder = sortperm(newOrder)
6-
return newOrder, oldOrder
7-
end
8-
9-
103
function sort_if_needed(x; kws...)
114
if issorted(x; kws...)
125
return x

test/test-pval-adjustment.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,18 +137,18 @@ using Test
137137
# inefficient implementation for testing
138138
function barber_candes_brute_force(pValues::AbstractVector{T}) where T <: AbstractFloat
139139
n = length(pValues)
140-
sorted_indexes, original_order = MultipleTesting.reorder(pValues)
141-
sorted_pValues = pValues[sorted_indexes]
140+
sorted_indexes = sortperm(pValues)
141+
pAdjusted = pValues[sorted_indexes]
142142
estimated_fdrs = fill(1.0, size(pValues))
143-
for (i, pv) in enumerate(sorted_pValues)
143+
for (i, pv) in enumerate(pAdjusted)
144144
if pv >= 0.5
145145
break
146146
else
147147
estimated_fdrs[i] = (sum((1 .- pValues) .<= pv) + 1) / i
148148
end
149149
end
150150
MultipleTesting.stepup!(estimated_fdrs)
151-
pAdjusted = clamp.(estimated_fdrs[original_order], 0, 1)
151+
pAdjusted[sorted_indexes] = clamp.(estimated_fdrs, 0, 1)
152152
return pAdjusted
153153
end
154154

test/test-utils.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ using Test
4343
end
4444

4545

46-
@testset "reorder" begin
46+
@testset "sorted and original ordering" begin
4747

4848
x = [1, 5, 4, 2, 4, 3]
49-
no, oo = MultipleTesting.reorder(x)
50-
@test x[no] == sort(x)
51-
@test x[no][oo] == x
49+
new_order = sortperm(x)
50+
y = x[new_order]
51+
z = copy(y)
52+
z[new_order] = y
53+
54+
@test y == sort(x)
55+
@test z == x
5256

5357
end
5458

0 commit comments

Comments
 (0)