Skip to content

Commit 8749d2e

Browse files
Merge branch 'dev' into changelog-v0.2.0
2 parents fe9244f + 15b312e commit 8749d2e

File tree

4 files changed

+87
-4
lines changed

4 files changed

+87
-4
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ The `MultipleTesting` package offers common algorithms for p-value adjustment an
1919
* Hommel
2020
* Sidak
2121
* Forward Stop
22+
* Barber-Candès
2223

2324
```julia
2425
adjust(pvals, Bonferroni())
@@ -32,6 +33,7 @@ adjust(pvals, Holm())
3233
adjust(pvals, Hommel())
3334
adjust(pvals, Sidak())
3435
adjust(pvals, ForwardStop())
36+
adjust(pvals, BarberCandes())
3537
```
3638

3739
The adjustment can also be performed on the `k` smallest out of `n` p-values:

src/MultipleTesting.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export
2727
Hommel,
2828
Sidak,
2929
ForwardStop,
30+
BarberCandes,
3031
qValues,
3132
estimate_pi0,
3233
Pi0Estimator,

src/pval-adjustment.jl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,70 @@ end
234234
forwardstop_step(p::AbstractFloat, i::Int, k::Int, n::Int) = p * 1/(k-i)
235235

236236

237+
238+
239+
240+
# Barber-Candès
241+
immutable BarberCandes <: PValueAdjustment
242+
end
243+
244+
function adjust(pvals::PValues, method::BarberCandes)
245+
n = length(pvals)
246+
247+
if n <= 1
248+
return ones(pvals) # unlike other p-adjust methods
249+
end
250+
251+
sorted_indexes, original_order = reorder(pvals)
252+
estimated_fdrs = pvals[sorted_indexes]
253+
254+
Rt = 1 # current number of discoveries
255+
Vt = 1 # estimated false discoveries at t
256+
257+
left_pv = estimated_fdrs[1]
258+
right_pv = estimated_fdrs[n]
259+
260+
while (left_pv < 0.5)
261+
while ((1-right_pv <= left_pv) & (right_pv >= 0.5) & (Vt+Rt <= n))
262+
estimated_fdrs[n-Vt+1] = 1.0;
263+
right_pv = estimated_fdrs[n-Vt]
264+
Vt +=1
265+
end
266+
estimated_fdrs[Rt] = Vt/Rt
267+
Rt += 1
268+
left_pv = estimated_fdrs[Rt]
269+
end
270+
271+
while ((right_pv >= 0.5) & (Vt + Rt <= n+1))
272+
estimated_fdrs[n-Vt+1] = 1.0;
273+
right_pv = (Vt + Rt <= n)?estimated_fdrs[n-Vt]:0.0
274+
Vt +=1
275+
end
276+
277+
stepup!(estimated_fdrs, identity_step, n, n) # just monotonize, no multiplier needed
278+
return min(estimated_fdrs[original_order], 1)
279+
end
280+
281+
# as test, inefficient implementation
282+
function barber_candes_brute_force{T<:AbstractFloat}(pvals::AbstractVector{T})
283+
n = length(pvals)
284+
sorted_indexes, original_order = reorder(pvals)
285+
sorted_pvals = pvals[sorted_indexes]
286+
estimated_fdrs = ones(pvals)
287+
for (i,pv) in enumerate(sorted_pvals)
288+
if pv >= 0.5
289+
break
290+
else
291+
estimated_fdrs[i] = (sum(1-pvals .<= pv)+1)/i
292+
end
293+
end
294+
stepup!(estimated_fdrs, identity_step, n, n) # just monotonize, no multiplier needed
295+
return min(estimated_fdrs[original_order], 1)
296+
end
297+
298+
identity_step(p::AbstractFloat, i::Int, k::Int, n::Int) = p
299+
300+
237301
## internal ##
238302

239303
# step-up / step-down

test/test-pval-adjustment.jl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ using Base.Test
1919
BenjaminiYekutieli => [0.0, 0.001464484, 0.009763228, 0.073224206, 0.292896825, 0.488161376, 0.836848073, 1.0, 1.0, 1.0],
2020
BenjaminiLiu => [0.0, 0.0008096761, 0.0063776447, 0.0475542565, 0.1589448656, 0.2047550000, 0.2361600000, 0.2361600000, 0.2361600000, 0.2361600000],
2121
Sidak => [0.0, 0.0009995501, 0.0099551198, 0.0956179250, 0.4012630608, 0.6513215599, 0.8926258176, 0.9939533824, 0.9999990463, 1.0000000000],
22-
ForwardStop => [0.0, 0.0000500025, 0.0003668351, 0.0027877103, 0.0124888271, 0.0279674419, 0.0558497432, 0.1127217283, 0.2542297986, 1.0]
22+
ForwardStop => [0.0, 0.0000500025, 0.0003668351, 0.0027877103, 0.0124888271, 0.0279674419, 0.0558497432, 0.1127217283, 0.2542297986, 1.0],
23+
BarberCandes => [0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.375, 1.0, 1.0]
2324
)
2425

2526
# p-values with ties
@@ -33,7 +34,8 @@ using Base.Test
3334
BenjaminiYekutieli => [0.001464484, 0.001464484, 0.009763228, 0.073224206, 0.292896825, 0.418424036, 0.418424036, 1.0, 1.0, 1.0],
3435
BenjaminiLiu => [0.0009995501, 0.0009995501, 0.0063776447, 0.0475542565, 0.1589448656, 0.2047550000, 0.2047550000, 0.2352000000, 0.2352000000, 0.2352000000],
3536
Sidak => [0.0009995501, 0.0009995501, 0.0099551198, 0.0956179250, 0.4012630608, 0.6513215599, 0.6513215599, 0.9939533824, 0.9999990463, 1.0000000000],
36-
ForwardStop => [0.0001000050, 0.0001000050, 0.0004001701, 0.0028127115, 0.0125088281, 0.0279841094, 0.0390378817, 0.0980113495, 0.2411539063, 1.0]
37+
ForwardStop => [0.0001000050, 0.0001000050, 0.0004001701, 0.0028127115, 0.0125088281, 0.0279841094, 0.0390378817, 0.0980113495, 0.2411539063, 1.0],
38+
BarberCandes => [0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.2857142857, 0.375, 1.0, 1.0]
3739
)
3840

3941
# smallest p-values from larger set
@@ -56,11 +58,16 @@ using Base.Test
5658
@test_throws DomainError adjust([0.5, 1.5], method())
5759

5860
## any single p-value is returned unchanged
59-
if method != ForwardStop # this test is not valid for ForwardStop
61+
if !(method in [ForwardStop, BarberCandes]) # this test is not valid for ForwardStop
6062
pval = rand(1)
6163
@test adjust(pval, method()) == pval
6264
end
6365

66+
if (method == BarberCandes)
67+
pval = rand(1)
68+
@test adjust(pval, method()) == ones(pval)
69+
end
70+
6471
## compare with reference values
6572
@test isapprox( adjust(pval1, method()), ref1[method], atol = 1e-9 )
6673
@test isapprox( adjust(PValues(pval1), method()), ref1[method], atol = 1e-9 )
@@ -91,7 +98,16 @@ using Base.Test
9198

9299
end
93100

101+
@testset "BarberCandès #2:" begin #some additional tests
102+
for k=1:5
103+
srand(k)
104+
pv = rand(BetaUniformMixtureModel(0.5, 0.5, 7.0), 40)
105+
@test isapprox(adjust(pv, BarberCandes()),
106+
MultipleTesting.barber_candes_brute_force(pv), atol=1e-9)
107+
end
108+
end
94109
end
95110

96-
end
97111

112+
113+
end

0 commit comments

Comments
 (0)