File Coverage

blib/lib/List/Vectorize/lib/Statistic.pl
Criterion Covered Total %
statement 216 224 96.4
branch 62 70 88.5
condition 21 29 72.4
subroutine 40 40 100.0
pod 24 24 100.0
total 363 387 93.8


line stmt bran cond sub pod time code
1            
2             #===== statistic functions ==================
3            
4             sub abs {
5            
6 32     32 1 80 check_prototype(@_, '$');
7            
8 32 100       151 $_[0] < 0 ? -$_[0] : $_[0];
9             }
10            
11             # usage: sign( [SCALAR] )
12             # return: SCALAR
13             sub sign {
14            
15 4     4 1 26 check_prototype(@_, '$');
16            
17 4 100       30 $_[0] > 0 ? 1 : $_[0] < 0 ? -1 : 0;
    100          
18             }
19            
20             # usage: sum( [ARRAY REF] )
21             # return: SCALAR
22             # description: ΗσΊΝ
23             sub sum {
24            
25 569     569 1 1590 check_prototype(@_, '\@');
26            
27 569         861 my $array = shift;
28            
29 569         640 my $sum = 0;
30 569         1680 for(my $i = 0; $i < len($array); $i ++) {
31 2162         6848 $sum += $array->[$i];
32             }
33            
34 569         2375 return $sum;
35             }
36            
37             # usage: mean( [ARRAY REF] )
38             # return: SCALAR
39             sub mean {
40            
41 125     125 1 350 check_prototype(@_, '\@');
42            
43 125         395 return sum($_[0]) / len($_[0]);
44             }
45            
46             # usage: geometric_mean( [ARRAY REF] )
47             # return: SCALAR
48             sub geometric_mean {
49            
50 2     2 1 21 check_prototype(@_, '\@');
51            
52 2     13   26 return exp(sum(sapply($_[0], sub{log($_[0])})) / len($_[0]));
  13         68  
53             }
54            
55             # usage: sd( [ARRAY REF] )
56             # return: SCALAR
57             sub sd {
58            
59 22     22 1 81 check_prototype(@_, '\@$?');
60            
61 22         69 return sqrt(var(@_));
62             }
63            
64             # usage: var( [ARRAY REF], [SCALAR] )
65             # return: SCALAR
66             sub var {
67            
68 26     26 1 92 check_prototype(@_, '\@$?');
69            
70 26         39 my $array = shift;
71 26         44 my $mean = shift;
72            
73 26 50       84 if(len($array) < 2) {
74 0         0 croak "ERROR: Length of the vector should be larger than 1.";
75             }
76            
77             # if the second argument was not specified
78 26 100 66     166 if(!defined($mean) or $mean eq "") {
79 18         46 $mean = mean($array);
80             }
81 26     208   186 return sum(sapply($array, sub {($_[0]-$mean)**2})) / $#$array;
  208         595  
82             }
83            
84             # usage: cov( [ARRAY REF], [ARRAY REF] )
85             # return: SCALAR
86             sub cov{
87            
88 9     9 1 30 check_prototype(@_, '\@\@');
89            
90 9         18 my $array1 = shift;
91 9         42 my $array2 = shift;
92            
93 9 50       39 if(len($array1) != len($array2)) {
94 0         0 croak "ERROR: Length of the two vectors should be same";
95             }
96            
97 9         39 my $mean1 = mean($array1);
98 9         27 my $mean2 = mean($array2);
99            
100 9     76   87 return sum(mapply($array1, $array2, sub {($_[0]-$mean1)*($_[1]-$mean2)})) / $#$array1;
  76         215  
101             }
102            
103             # usage: cor( [ARRAY REF], [ARRAY REF] )
104             # return: SCALAR
105             sub cor {
106            
107 10     10 1 53 check_prototype(@_, '\@\@$?');
108            
109 10         17 my $array1 = shift;
110 10         14 my $array2 = shift;
111 10   100     44 my $method = shift || "pearson";
112            
113 10 100       28 if($method eq "spearman") {
114 3         18 return cor(rank($array1), rank($array2));
115             }
116             else {
117 7         35 return cov($array1, $array2)/sd($array1)/sd($array2);
118             }
119             }
120            
121             # usage: dist( [ARRAY REF], [ARRAY REF], [SCALAR] )
122             # return: SCALAR
123             sub dist {
124            
125 7     7 1 45 check_prototype(@_, '\@\@$?');
126            
127 7         15 my $vector1 = shift;
128 7         11 my $vector2 = shift;
129 7   100     28 my $method = shift || "euclidean";
130            
131 7 50       26 if(len($vector1) != len($vector2)) {
132 0         0 croak "ERROR: Length of the two vectors should be same";
133             }
134            
135 7         19 $method = lc($method);
136 7 100       35 if($method eq "euclidean") {
    100          
    100          
    50          
137 3     24   23 return sqrt(sum(mapply($vector1, $vector2, sub{($_[0]+$_[1])**2})));
  24         58  
138             }
139             elsif($method eq "pearson") {
140 1         3 return 1 - cor($vector1, $vector2);
141             }
142             elsif($method eq "spearman") {
143 2         9 return 1 - cor($vector1, $vector2, "spearman");
144             }
145             elsif($method eq "logical") {
146 1 100   7   7 return 1/(1+sum(mapply($vector1, $vector2, sub{($_[0] && $_[1])})));
  7         23  
147             }
148            
149             }
150            
151             # usage: freq( [ARRAY REF], [ARRAY REF], ... )
152             # return: HASH REF
153             sub freq {
154            
155 8     8 1 696 check_prototype(@_, '(\@)+');
156            
157 8         22 my @category = @_;
158            
159 8         19 my $f = {};
160 8         37 my $category = paste(@category, "|");
161 8         38 foreach (@$category) {
162 48         89 $f->{$_} ++;
163             }
164 8         40 return $f;
165             }
166            
167             # same as frequency
168             sub table {
169            
170 4     4 1 1030 check_prototype(@_, '(\@)+');
171            
172 4         14 return freq(@_);
173             }
174            
175             # usage: scale( [ARRAY REF], "zvalue|percentage|sphere")
176             # return: HASH REF
177             sub scale {
178            
179 6     6 1 1401 check_prototype(@_, '\@$?');
180            
181 6         11 my $array = shift;
182 6   100     22 my $type = shift || "zvalue";
183 6         11 $type = lc($type);
184            
185 6 100       22 if($type eq "percentage") {
    100          
186 1         4 my $max = max($array);
187 1         5 my $min = min($array);
188            
189 1     10   9 return sapply($array, sub { ($_[0] - $min)/($max - $min) });
  10         95  
190             }
191             elsif($type eq "sphere") { # all the points are on the surface of the unit super sphere
192 1     10   7 my $s = sqrt(sum(sapply($array, sub{$_[0]**2})));
  10         20  
193            
194 1     10   11 return sapply($array, sub { $_[0]/$s });
  10         20  
195             }
196             else {
197 4         13 my $mean = mean($array);
198 4         12 my $sd = sd($array, $mean);
199            
200 4     40   32 return sapply($array, sub { ($_[0] - $mean)/$sd });
  40         69  
201             }
202             }
203            
204             # usage: sample( [ARRAY REF], [SCALAR], "p" => [ARRAY REF], "replace" => 0|1 )
205             # return: ARRAY REF
206             sub sample {
207            
208 6     6 1 38 check_prototype(@_, '\@($|\@|\%)+');
209            
210 6         13 my $array = shift;
211 6         11 my $size = shift;
212            
213 6         27 my $setup = {"p" => repeat(1, len($array)),
214             "replace" => 0,
215             @_ };
216 6         24 my $sum_p = sum($setup->{"p"});
217 6     52   61 my $p = sapply($setup->{"p"}, sub {$_[0]/$sum_p}); #0-1
  52         116  
218 6         28 my $replace = $setup->{"replace"};
219            
220 6 50 66     34 if(!$replace and len($array) < $size) {
221 0         0 croak "ERROR: Size($size) should not be bigger than the sample size with no replacement.\n";
222             }
223            
224 6 50       22 if(len($array) != len($p)) {
225 0         0 croak "ERROR: Length of the vector should be same as the length of the probability.\n";
226             }
227            
228 6         13 my $sample = [];
229 6         15 my $ecdf = _ecdf($p);
230            
231 6 100       22 if($replace) {
232 3         39 for(my $i = 0; $i < $size; $i ++) {
233 22         49 my $ind = _get_index_from_p(rand(), $ecdf);
234 22         69 push(@$sample, $array->[$ind]);
235             }
236             }
237             else {
238 3         6 my $array_copy;
239 3         15 push(@$array_copy, @$array);
240 3         5 my $p_copy;
241 3         117 push(@$p_copy, @$p);
242            
243 3         9 my $p_sum = sum($p_copy);
244 3         12 for(my $i = 0; $i < $size; $i ++) {
245 17         143 my $ind = _get_index_from_p(rand(), $ecdf);
246 17         43 push(@$sample, $array_copy->[$ind]);
247 17         29 $p_sum -= $p_copy->[$ind];
248            
249 17         54 $array_copy = del_array_item($array_copy, $ind);
250 17         45 $p_copy = del_array_item($p_copy, $ind);
251            
252 17     97   109 $p_copy = sapply($p_copy, sub{$_[0]/$p_sum});
  97         197  
253 17         220 $ecdf = _ecdf($p_copy);
254             }
255             }
256 6         66 return $sample;
257             }
258            
259             # usage: ecdf( [ARRAY REF])
260             # return: ARRAY REF
261             sub _ecdf {
262 24     24   45 my $p = shift;
263 24         67 my $ecdf = cumf($p, \&sum);
264 24         105 return $ecdf;
265             }
266            
267             # usage: _get_index_from_p( [SCALAR], [ARRAY REF])
268             # return: SCALAR
269             # [, )
270             sub _get_index_from_p {
271 44     44   3117 my $r = shift;
272 44         51 my $p = shift;
273             # the first
274 44 100       111 if($r < $p->[0]) {
275 3         9 return 0;
276             }
277 41         126 for(my $k = 0; $k < len($p)-1; $k ++) {
278 132 100 66     717 if($r >= $p->[$k] and $r < $p->[$k+1]) {
279 35         77 return $k+1;
280             }
281             }
282 6         17 return len($p)-1;
283             }
284            
285            
286             # usage: rnorm( [SCALAR], [SCALAR], [SCALAR])
287             # return: ARRAY REF
288             sub rnorm {
289            
290 5     5 1 32 check_prototype(@_, '${1,3}');
291            
292 5         10 my $size = shift;
293 5         9 my $mean = shift;
294 5         8 my $sd = shift;
295            
296 5 100 66     32 $mean = (defined($mean) and $mean ne "") ? $mean : 0;
297 5 100 66     27 $sd = (defined($sd) and $sd ne "") ? $sd : 1;
298            
299 5         11 my $r = [];
300 5         16 for(my $i = 0; $i < $size; $i ++) {
301 50         137 my $yita1 = rand(1);
302 50         51 my $yita2 = rand(1);
303 50         108 while($yita1 == 0) {
304 0         0 $yita1 = rand(1);
305             }
306 50         97 while($yita2 == 0) {
307 0         0 $yita2 = rand(1);
308             }
309 50         198 my $x = sqrt(-2*log($yita1)/log(exp(1)))*sin(2*3.1415926*$yita2);
310 50         66 $x = $mean + $sd*$x;
311 50         149 push(@$r, $x);
312             }
313            
314 5         99 return $r;
315             }
316            
317             # usage: rbinom( [SCALAR], [SCALAR])
318             # return: ARRAY REF
319             sub rbinom {
320            
321 4     4 1 27 check_prototype(@_, '$+');
322            
323 4         8 my $size = shift;
324 4         7 my $p = shift;
325            
326 4 100 66     54 $p = (defined($p) and $p ne "") ? $p : 0.5;
327            
328 4         19 my $d = initial_array($size);
329 4 100   40   29 $d = sapply($d, sub { rand() < $p ? 1 : 0});
  40         168  
330 4         28 return $d;
331             }
332            
333             # usage: max( [ARRAY REF] )
334             # return: SCALAR
335             sub max {
336            
337 145     145 1 6186 check_prototype(@_, '\@');
338            
339 145         292 my $array = shift;
340 145         245 my $max = $array->[0];
341 145         310 for (@$array) {
342 502 100       1036 $max = $max > $_ ? $max : $_;
343             }
344 145         433 return $max;
345             }
346            
347             # usage: min( [ARRAY REF] )
348             # return: SCALAR
349             sub min {
350            
351 56     56 1 176 check_prototype(@_, '\@');
352            
353 56         85 my $array = shift;
354 56         94 my $min = $array->[0];
355 56         113 for (@$array) {
356 283 100       545 $min = $min < $_ ? $min : $_;
357             }
358 56         179 return $min;
359             }
360            
361             # usage: which_max( [ARRAY REF] )
362             # return: SCALAR
363             sub which_max {
364            
365 2     2 1 20 check_prototype(@_, '\@');
366            
367 2         6 my $array = shift;
368 2         11 my $max = $array->[0];
369 2         6 my $which_max = 0;
370 2         9 for (1..$#$array) {
371 18 50       38 if($array->[$_] > $max) {
372 18         24 $which_max = $_;
373             }
374             }
375 2         28 return $which_max;
376             }
377            
378             # usage: which_min( [ARRAY REF] )
379             # return: SCALAR
380             sub which_min {
381            
382 2     2 1 22 check_prototype(@_, '\@');
383            
384 2         4 my $array = shift;
385 2         6 my $min = $array->[0];
386 2         4 my $which_min = 0;
387 2         7 for (1..$#$array) {
388 12 100       26 if($array->[$_] < $min) {
389 3         6 $which_min = $_;
390             }
391             }
392 2         20 return $which_min;
393             }
394            
395             # usage: median( [ARRAY REF] )
396             # return: SCALAR
397             sub median {
398            
399 3     3 1 27 check_prototype(@_, '\@');
400            
401 3         6 my $array = shift;
402            
403 3         15 my $sort = sort_array($array);
404            
405 3         8 my $median_index;
406 3 100       88 if(len($sort) % 2 == 1) {
407 1         4 $median_index = int(scalar(@$sort) / 2);
408 1         9 return $sort->[$median_index];
409             }
410             else {
411 2         19 return ($sort->[int(scalar(@$sort) / 2)] + $sort->[int(scalar(@$sort) / 2) - 1])/2;
412             }
413             }
414            
415             # usage: quantile( [ARRAY REF], [SCALAR | ARRAY REF] )
416             # return: SCALAR | ARRAY REF
417             sub quantile {
418            
419 35     35 1 2302 check_prototype(@_, '\@($|\@)?');
420            
421 35         116 my $array = shift;
422 35         46 my $p = shift;
423 35 100       76 $p = defined($p) ? $p : [0, 0.25, 0.5, 0.75, 1];
424 35         39 my $q;
425            
426 35 100       127 if(is_array_ref($p)) {
427 8     25   105 return sapply($p, sub{ quantile($array, $_[0]) });
  25         91  
428             }
429             else {
430 27 50 33     139 if($p > 1 or $p < 0) {
431 0         0 croak "P value should in 0~1";
432             }
433 27         83 $array = sort_array($array);
434 27 100       76 if($p == 1) {
435 3         14 return $array->[$#$array];
436             }
437 24 100       54 if($p == 0) {
438 3         15 return $array->[0];
439             }
440            
441 21         68 my $n = len($array);
442            
443 21 100       96 if(&abs(int($p*($n-1)) - $p*($n-1)) < EPS) {
444 7         36 return $array->[int($p*($n-1))];
445             }
446             else {
447 14         36 my $floor = $array->[int($p*($n-1)+EPS)];
448 14         30 my $ceiling = $array->[int($p*($n-1)+EPS) + 1];
449 14         79 return ($p*($n-1)-int($p*($n-1)+EPS))*($ceiling - $floor) + $floor;
450             }
451             }
452             }
453            
454            
455             # usage: iqr( [ARRAY REF])
456             # return: SCALAR
457             sub iqr {
458            
459 3     3 1 28 check_prototype(@_, '\@');
460            
461 3         8 my $array = shift;
462            
463 3         17 my $q = quantile($array, [.25, .75]);
464 3         33 return $q->[1] - $q->[0];
465             }
466            
467             sub cumf {
468            
469 29     29 1 2263 check_prototype(@_, '\@(\&)?');
470            
471 29         54 my $array = shift;
472 29   100 20   90 my $function = shift || sub {my $x = $_[0]; $x->[$#$x];};
  20         45  
  20         90  
473            
474 29         47 my $cum = [];
475 29         40 my $carray = [];
476 29         103 for(my $i = 0; $i < len($array); $i ++) {
477 203         476 push(@$carray, $array->[$i]);
478 203         367 $cum->[$i] = $function->($carray);
479             }
480 29         104 return $cum;
481             }
482            
483            
484             1;
485            
486