File Coverage

blib/lib/Statistics/Multtest.pm
Criterion Covered Total %
statement 186 197 94.4
branch 26 38 68.4
condition 12 24 50.0
subroutine 32 32 100.0
pod 0 6 0.0
total 256 297 86.2


line stmt bran cond sub pod time code
1             package Statistics::Multtest;
2            
3 6     6   139382 use List::Vectorize;
  6         121108  
  6         2390  
4 6     6   643 use Carp;
  6         11  
  6         440  
5 6     6   46 use strict;
  6         11  
  6         244  
6 6     6   31 use constant {EPS => 1e-10};
  6         19  
  6         992  
7            
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our @EXPORT_OK = qw(bonferroni holm hommel hochberg BH BY qvalue);
11             our %EXPORT_TAGS = (all => [qw(bonferroni holm hommel hochberg BH BY qvalue)]);
12            
13             our $VERSION = '0.13';
14            
15             1;
16            
17             BEGIN {
18 6     6   35 no strict 'refs';
  6         14  
  6         1035  
19 6     6   18 for my $adjp (qw(bonferroni holm hommel hochberg BH BY qvalue)) {
20 42         20132 *{$adjp} = sub {
21 20     20   52867 my $p = shift;
22 20         38 my $name;
23 20         63 ($name, $p) = initial($p);
24 20         36 *private = *{"_$adjp"};
  20         112  
25 20         64 my $adjp = private($p, @_);
26 18         3171 return get_result($adjp, $name);
27             }
28 42         173 }
29             }
30            
31             sub initial {
32 20     20 0 29 my $p = shift;
33            
34 20 50 66     70 if(! List::Vectorize::is_array_ref($p) and ! List::Vectorize::is_hash_ref($p)) {
35 0         0 croak "ERROR: P-values should be stored in an array reference or a hash reference.\n";
36             }
37            
38 20         255 my $name = [];
39 20 100       60 if(List::Vectorize::is_hash_ref($p)) {
40 9         61 $name = [ keys %{$p} ];
  9         81  
41 9         24 $p = [ values %{$p} ];
  9         38  
42             }
43            
44 20 50 33     138 if(max($p) > 1 or min($p) < 0) {
45 0         0 croak "ERROR: P-values should between 0 and 1.\n";
46             }
47            
48 20         2454 return ($name, $p);
49             }
50            
51             sub get_result {
52 18     18 0 40 my ($adjp, $name) = @_;
53            
54 18 100       64 if(is_empty($name)) {
55 10         255 return $adjp;
56             }
57             else {
58 8         109 my $result;
59 8         22 for (0..$#$name) {
60 82         194 $result->{$name->[$_]} = $adjp->[$_];
61             }
62 8         67 return $result;
63             }
64             }
65            
66             # R code: pmin(1, n * p)
67             sub _bonferroni {
68 2     2   4 my $p = shift;
69 2         10 my $n = len($p);
70            
71 2         30 my $adjp = multiply($n, $p);
72            
73 2         2567 return pmin(1, $adjp);
74             }
75            
76             # R code: i = 1:n
77             # o = order(p)
78             # ro = order(o)
79             # pmin(1, cummax((n - i + 1) * p[o]))[ro]
80             sub _holm {
81 2     2   4 my $p = shift;
82 2         7 my $n = len($p);
83            
84 2         28 my $i = seq(1, $n);
85 2         154 my $o = order($p);
86 2         193 my $ro = order($o);
87            
88 2         139 my $adjp = multiply(minus($n + 1, $i), subset($p, $o));
89 2         4925 $adjp = cumf($adjp, \&max);
90 2         980 $adjp = pmin(1, $adjp);
91            
92 2         127 return subset($adjp, $ro);
93             }
94            
95             # R code: i = 1:n
96             # o = order(p)
97             # p = p[o]
98             # ro = order[o]
99             # q = pa = rep.int(min(n * p/i), n)
100             # for (j in (n - 1):2) {
101             # ij = 1:(n - j + 1)
102             # i2 = (n - j + 2):n
103             # q1 <- min(j * p[i2]/(2:j))
104             # q[ij] <- pmin(j * p[ij], q1)
105             # q[i2] <- q[n - j + 1]
106             # pa <- pmax(pa, q)
107             # }
108             # pmax(pa, p)[ro]
109             sub _hommel {
110 2     2   10 my $p = shift;
111 2         31 my $n = len($p);
112            
113 2         25 my $i = seq(1, $n);
114 2         142 my $o = order($p);
115 2         209 $p = subset($p, $o);
116 2         520 my $ro = order($o);
117            
118 2         172 my $pa = rep(min(divide(multiply($n, $p), $i)), $n);
119 2         4760 my $q = copy($pa);
120            
121             # set the first index as 1
122 2         824 unshift(@$p, 0);
123 2         7 unshift(@$q, 0);
124 2         4 unshift(@$pa, 0);
125            
126 2         6 my $ij;
127             my $i2;
128 0         0 my $q1;
129 2         4 for my $j (@{seq($n - 1, 2)}) {
  2         8  
130            
131 16         983 $ij = seq(1, $n - $j + 1);
132 16         992 $i2 = seq($n - $j + 2, $n);
133 16         755 $q1 = min(divide(multiply($j, subset($p, $i2)), seq(2, $j)));
134 16         32279 subset_value($q, $ij, pmin(multiply($j, subset($p, $ij)), $q1));
135 16         5310 subset_value($q, $i2, $q->[$n - $j + 1]);
136 16         6013 $pa = pmax($pa, $q);
137             }
138            
139 2         114 shift(@$p);
140 2         4 shift(@$q);
141 2         5 shift(@$pa);
142            
143 2         7 my $adjp = pmax($pa, $p);
144 2         110 return subset($adjp, $ro);
145             }
146            
147             # R code: i = n:1
148             # o <- order(p, decreasing = TRUE)
149             # ro <- order(o)
150             # pmin(1, cummin((n - i + 1) * p[o]))[ro]
151             sub _hochberg {
152            
153 2     2   4 my $p = shift;
154 2         8 my $n = len($p);
155 2         25 my $i = seq($n, 1);
156            
157 2     44   153 my $o = order($p, sub {$_[1] <=> $_[0]});
  44         282  
158 2         25 my $ro = order($o);
159            
160 2         165 my $adjp = multiply(minus($n+1, $i), subset($p, $o));
161 2         5362 $adjp = cumf($adjp, \&min);
162 2         1070 $adjp = pmin(1, $adjp);
163 2         128 return subset($adjp, $ro);
164             }
165            
166             # R code: i <- n:1
167             # o <- order(p, decreasing = TRUE)
168             # ro <- order(o)
169             # pmin(1, cummin(n/i * p[o]))[ro]
170             sub _BH {
171 2     2   4 my $p = shift;
172 2         7 my $n = len($p);
173 2         24 my $i = seq($n, 1);
174            
175 2     44   142 my $o = order($p, sub {$_[1] <=> $_[0]});
  44         263  
176 2         12 my $ro = order($o);
177            
178 2         136 my $adjp = multiply(divide($n, $i), subset($p, $o));
179 2         5184 $adjp = cumf($adjp, \&min);
180 2         1147 $adjp = pmin(1, $adjp);
181 2         123 return subset($adjp, $ro);
182            
183             }
184            
185             # R code: i <- n:1
186             # o <- order(p, decreasing = TRUE)
187             # ro <- order(o)
188             # q <- sum(1/(1L:n))
189             # pmin(1, cummin(q * n/i * p[o]))[ro]
190             sub _BY {
191            
192 2     2   4 my $p = shift;
193 2         8 my $n = len($p);
194 2         24 my $i = seq($n, 1);
195            
196 2     44   153 my $o = order($p, sub {$_[1] <=> $_[0]});
  44         277  
197 2         19 my $ro = order($o);
198            
199 2         156 my $q = sum(divide(1, seq(1, $n)));
200 2         3508 my $adjp = multiply(divide($q*$n, $i), subset($p, $o));
201 2         5468 $adjp = cumf($adjp, \&min);
202 2         1052 $adjp = pmin(1, $adjp);
203 2         128 return subset($adjp, $ro);
204             }
205            
206             sub pmin {
207 26     26 0 21100 my $array1 = shift;
208 26         74 my $array2 = shift;
209            
210 26     188   131 return mapply($array1, $array2, sub {min(\@_)});
  188         41586  
211             }
212            
213             sub pmax {
214 18     18 0 28 my $array1 = shift;
215 18         24 my $array2 = shift;
216            
217 18     196   89 return mapply($array1, $array2, sub {max(\@_)});
  196         28226  
218             }
219            
220            
221             # the default setting can not assure the success of calculation
222             # so this function should run under eval
223             # eval(q(qvalue $p));
224             # if ($@) change_other_settings
225             # @p = qw(0.76 0.30 0.40 0.43 0.27 0.79 0.66 0.36 0.12 0.16 0.52 0.04 0.67 0.44 0.40 0.09 0.51 0.38 0.49 0.68)
226             sub _qvalue {
227 8     8   16 my $p = shift;
228 8         33 my %setup = ('lambda' => multiply(seq(0, 90, 5), 0.01),
229             'robust' => 0,
230             @_ );
231            
232 8         15665 my $lambda = $setup{'lambda'};
233 8         20 my $robust = $setup{'robust'};
234            
235 8 50       27 if(! List::Vectorize::is_array_ref($lambda)) {
236 0         0 croak "ERROR: lambda should be an array reference. If your lambda is a single number, you should set it as an array reference containing one element, such as [0.1].";
237             }
238            
239 8 50 66     80 if(len($lambda) > 1 and len($lambda) < 4) {
240 0         0 croak "ERROR: If length of lambda greater than 1, you need at least 4 values.";
241             }
242 8 50 33     165 if(len($lambda) > 1 and (min($lambda) < 0 or max($lambda) >= 1)) {
      66        
243 0         0 croak "ERROR: Lambda must be within [0, 1).";
244             }
245            
246             # m <- length(p)
247 8         819 my $m = len($p);
248 8         79 my $pi0;
249            
250 8 100       22 if(len($lambda) == 1) {
251 2         19 $lambda = $lambda->[0];
252 2 50 33     16 if($lambda < 0 or $lambda >= 1) {
253 0         0 croak "ERROR: Lambda must be within [0, 1).";
254             }
255             # pi0 <- mean(p >= lambda)/(1-lambda)
256 2     20   14 $pi0 = mean(test($p, sub {_approximately_compare($_[0], ">=", $lambda)})) / (1 - $lambda);
  20         254  
257             # pi0 <- min(pi0,1)
258 2         367 $pi0 = min(c($pi0, 1));
259             }
260             else {
261             # pi0 <- rep(0,length(lambda))
262 6         65 $pi0 = rep(0, len($lambda));
263             # for(i in 1:length(lambda)) {
264             # pi0[i] <- mean(p >= lambda[i])/(1-lambda[i])
265             # }
266             $pi0 = sapply(seq(0, len($lambda) - 1),
267             sub {
268 114     114   24351 my $current_lambda = $lambda->[$_[0]];
269 114         864 mean(test($p, sub {_approximately_compare($_[0], ">=", $current_lambda)})) / (1 - $current_lambda);
  1197         18375  
270 6         997 });
271            
272             # minpi0 <- min(pi0)
273 6         1128 my $minpi0 = min($pi0);
274            
275             # mse <- rep(0,length(lambda))
276 6         323 my $mse = rep(0, len($lambda));
277             # pi0.boot <- rep(0,length(lambda))
278 6         1092 my $pi0_boot = rep(0, len($lambda));
279 6         623 for (1..100) {
280             # p.boot <- sample(p,size=m,replace=TRUE)
281 600         894920 my $p_boot = sample($p, $m, 'replace' => 1);
282             # for(i in 1:length(lambda)) {
283             # pi0.boot[i] <- mean(p.boot>lambda[i])/(1-lambda[i])
284             # }
285             $pi0_boot = sapply(seq(0, len($lambda) - 1),
286             sub {
287 11400     11400   2261607 my $current_lambda = $lambda->[$_[0]];
288 11400         55030 mean(test($p_boot, sub {$_[0] > $current_lambda})) / (1 - $current_lambda);
  119700         1810943  
289 600         1349899 });
290             # mse <- mse + (pi0.boot-minpi0)^2
291 600         133890 $mse = plus($mse, power(minus($pi0_boot, $minpi0), 2));
292             }
293            
294             # pi0 <- min(pi0[mse==min(mse)])
295 6         8861 my $min_mse = min($mse);
296            
297 6     114   487 $pi0 = min(subset($pi0, which(test($mse, sub {_approximately_compare($_[0], "==", $min_mse)}))));
  114         1540  
298 6         1949 $pi0 = min(c($pi0, 1));
299             }
300            
301 8 100       1214 if($pi0 <= 0) {
302 2         704 croak "ERROR: The estimated pi0 ($pi0) <= 0. Check that you have valid p-values or use another lambda method.";
303             }
304            
305             # u <- order(p)
306 6         26 my $u = order($p);
307            
308             # this function is not the same to the function in qvalue
309             # but returns the same result
310             # returns number of observations less than or equal to
311             sub qvalue_rank {
312 6     6 0 12 my $x = shift;
313             return sapply($x, sub {
314 62     62   8855 my $current_x = $_[0];
315 62         648 sum(test($x, sub {$_[0] <= $current_x}));
  642         8780  
316 6         35 });
317             }
318            
319             # v <- qvalue.rank(p)
320 6         677 my $v = qvalue_rank($p);
321             # qvalue <- pi0*m*p/v
322 6         904 my $qvalue = divide(multiply($pi0, $m, $p), $v);
323            
324 6 100       25338 if($robust) {
325             # qvalue <- pi0*m*p/(v*(1-(1-p)^m))
326 2         13 $qvalue = divide(multiply($pi0, $m, $p), multiply($v, minus(1, power(minus(1, $p), $m))));
327             }
328            
329             # qvalue[u[m]] <- min(qvalue[u[m]],1)
330 6         6190 $qvalue->[$u->[$#$u]] = min(c($qvalue->[$u->[$#$u]], 1));
331             # for(i in (m-1):1) {
332             # qvalue[u[i]] <- min(qvalue[u[i]],qvalue[u[i+1]],1)
333             # }
334 6         933 for my $i (@{seq($#$u-1, 0)}) {
  6         29  
335 56         2362 $qvalue->[$u->[$i]] = min([$qvalue->[$u->[$i]], $qvalue->[$u->[$i+1]], 1]);
336             }
337            
338 6         238 return $qvalue;
339             }
340            
341             # vectorized power calculation
342             sub power {
343 602     602 0 1188457 my $array = shift;
344 602         1240 my $power = shift;
345            
346 602     11420   3257 my $res = sapply($array, sub {$_[0]**($power)});
  11420         70869  
347 602         7761 return $res;
348             }
349            
350             # approximately
351             sub _approximately_compare {
352 1331     1331   3282 my $left = shift;
353 1331         1304 my $sign = shift;
354 1331         1852 my $right = shift;
355            
356 1331 100 66     5991 if($sign eq ">" or $sign eq ">=") {
    50 33        
    50          
357 1217 100       2820 if($left > $right) {
    100          
358 593         1305 return 1;
359             }
360             elsif(abs($left - $right) < EPS) {
361 38         1255 return 1;
362             }
363             else {
364 586         19715 return 0;
365             }
366             }
367             elsif($sign eq "<" or $sign eq "<=") {
368 0 0       0 if($left < $right) {
    0          
369 0         0 return 1;
370             }
371             elsif(abs($left - $right) < EPS) {
372 0         0 return 1;
373             }
374             else {
375 0         0 return 0;
376             }
377             }
378             elsif($sign eq "==") {
379 114 100       314 if(abs($left - $right) < EPS) {
380 26         737 return 1;
381             }
382             else {
383 88         2592 return 0;
384             }
385             }
386             }
387            
388             __END__