File Coverage

blib/lib/MyBioinfo/Common.pm
Criterion Covered Total %
statement 24 486 4.9
branch 0 138 0.0
condition 0 39 0.0
subroutine 8 48 16.6
pod 0 40 0.0
total 32 751 4.2


line stmt bran cond sub pod time code
1             package MyBioinfo::Common;
2              
3 1     1   28 use 5.006;
  1         4  
  1         50  
4 1     1   7 use strict;
  1         2  
  1         39  
5 1     1   7 use warnings;
  1         2  
  1         43  
6 1     1   6 use constant INFINITE => 'Inf';
  1         2  
  1         96  
7 1     1   7 use constant EPSILON => 1e-8;
  1         2  
  1         45  
8 1     1   1149 use POSIX qw(floor ceil);
  1         9123  
  1         7  
9 1     1   2468 use Math::CDF;
  1         2  
  1         66  
10             require MyShortRead::SRBed;
11 1     1   2282 use Data::Dumper;
  1         14856  
  1         5532  
12             require Exporter;
13              
14             our @ISA = qw(Exporter);
15              
16             # Items to export into callers namespace by default. Note: do not export
17             # names by default without a very good reason. Use EXPORT_OK instead.
18             # Do not simply export all your public functions/methods/constants.
19              
20             # This allows declaration use MyBioinfo::Common ':all';
21             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
22             # will save memory.
23             our %EXPORT_TAGS = ( 'all' => [ qw(
24            
25             ) ] );
26              
27             our @EXPORT_OK = qw(mean_r mad padjBH raw_sum2 raw_sum_mean raw_sum_var MchooseN BH_fdr raw_sum_dir raw_sum nb_pval_v2 nb_pval raw_mean_dir raw_mean nb_stat var fold_change chi_stat readnamelist readnamewithinfolist array2hash max min sum mean median log2 log10 read_norm2 rescale_cutoff read_cutoff isAboveCutoff rescale_norm_max rescale_norm_sum1 is_all_zero fprecision unique);
28              
29             our @EXPORT = qw(padjBH fold_change max min sum mean geomean var median log2 log10 read_norm2 is_all_zero fprecision INFINITE EPSILON);
30              
31             our $VERSION = '0.61';
32              
33              
34             ######## Preloaded methods go here. ##############
35              
36             # Imitate the R unique function.
37             sub unique{
38 0     0 0   my %ut;
39 0           foreach(@_){
40 0           $ut{$_} = 1;
41             }
42 0           return keys %ut;
43             }
44              
45             # Given a vector of P-values, return the adjusted P-values
46             # according to the BH procedure.
47             sub padjBH {
48 0     0 0   my $p = shift; # reference to P-value vector.
49 0           my $n; # number of tests to multiply.
50 0 0         if(@_ > 0) {$n = shift;}
  0            
  0            
51 0           else {$n = @{$p};}
52 0           my %p_BH; # hash to adjusted P-values.
53             # Store indices and P-values into hash.
54 0           for my $i(0..$#{$p}){
  0            
55 0           $p_BH{$i} = $p->[$i];
56             }
57             # Find the sorted indices by raw P-values. This determines the ranks of the P-values.
58 0           my @sorted_r = sort {$p_BH{$a} <=> $p_BH{$b}} keys %p_BH;
  0            
59             # Apply BH formula to all P-values.
60             # Create a reverse hash from the sorted indices to raw P-value rank at the same time.
61 0           my %rev_r;
62 0           for my $r(1..@sorted_r){
63 0           my $f = $p_BH{$sorted_r[$r-1]} * $n / $r; # BH formula.
64 0 0         $p_BH{$sorted_r[$r-1]} = $f > 1? 1.0 : $f; # truncate values larger than 1.0
65 0           $rev_r{$sorted_r[$r-1]} = $r-1; # hash: original position -> raw P-value rank.
66             }
67             # Now, find the sorted indices by BH'ed P-values.
68 0           my @sorted_rr = sort {$p_BH{$a} <=> $p_BH{$b}} keys %p_BH;
  0            
69             # Go through the 2nd list of sorted indices, solve the inconsistent P-values.
70 0           my $sta_r = 0; # remember starting raw P-value rank to be adjusted.
71 0           my $raw_r = 0; # current raw P-value rank.
72 0           for my $i(0..$#sorted_rr){
73             # Sequ: Iterator -> original position -> raw P-value rank.
74 0 0         if($rev_r{$sorted_rr[$i]} > $raw_r){ # Found a new min P-value. Or else, it is already adjusted.
75 0           $raw_r = $rev_r{$sorted_rr[$i]}; # update current rank.
76 0           for my $j($sta_r..$raw_r-1){
77             # use the raw P-value rank to find the original position to be fixed.
78 0           $p_BH{$sorted_r[$j]} = $p_BH{$sorted_rr[$i]};
79             }
80 0           $sta_r = $raw_r + 1; # advance from current rank.
81             }
82             }
83             # Return the adjusted P-values.
84 0           my @padj;
85 0           for my $i(0..$#{$p}) {push @padj, $p_BH{$i};}
  0            
  0            
86 0           return @padj;
87             }
88              
89             sub raw_sum_mean{
90 0     0 0   my ($m1,$m2,$norm_ref)=@_;
91 0           my $v = 0;
92 0           for my $i(@{$norm_ref})
  0            
93             {
94 0           $v +=1/$i;
95             }
96            
97 0           return $v*($m1+$m2)/2;
98             }
99              
100             sub raw_sum_var{
101 0     0 0   my ($base_var,$m1,$m2,$norm,$raw_mean,$eps)=@_;
102 0           my $base_mean = ($m1+$m2)/2;
103 0           my $z =0;
104 0           my $s = 0;
105 0           for my $i(@{$norm})
  0            
106             {
107 0           $z +=$i;
108 0           $s += (1/$i)**2;
109             }
110 0           $z = $z/@{$norm};
  0            
111            
112 0           my $var = $base_var - $z * $base_mean;
113 0 0         if($var < $eps*$base_mean)
114             {
115 0           $var = $eps*$base_mean;
116             }
117            
118 0           return $var*$s + $raw_mean;
119            
120             }
121              
122              
123             sub MchooseN
124             {
125 0     0 0   my ($m,$n,$ref) = @_;
126 0           my @items = @{$ref};
  0            
127            
128 0           my $k = $m-$n;
129 0           my @res = ();
130 0 0         if($n==0)
131             {
132 0           return @res;
133             }
134 0 0         if($n==1)
135             {
136 0           foreach my $v(@items)
137             {
138 0           my @val = ();
139 0           push @val,$v;
140 0           push @res,[@val];
141             }
142 0           return @res;
143             }
144             else
145             {
146             #to avoid the duplicated combination, treat the items as ordered
147 0           for my $i(0..$k)
148             {
149 0           my @left=();
150 0           for my $j($i+1..$#items)
151             {
152 0           push @left,$items[$j];
153             }
154 0           my @ret = MchooseN($m-1,$n-1,\@left);
155 0           my $len = @ret;
156            
157 0           for(my $j=0;$j<$len;$j++)
158             {
159 0           my @val = ();
160 0           push @val, $items[$i];
161 0           push @val, @{$ret[$j]};
  0            
162            
163 0           push @res,[@val];
164             }
165             }
166 0           return @res;
167             }
168             }
169              
170             sub BH_fdr{
171 0     0 0   my ($p,$c,$threshold,$res) = @_;
172 0           my $index = 1;
173 0           my $max_id =0;
174 0           my $min = 1.0;
175            
176 0           foreach my $key (sort {$p->{$a} <=> $p->{$b}} keys %{$p} )
  0            
  0            
177             {
178 0           my $v = $p->{$key};
179 0           $v = $v * $c/$index;
180            
181 0           $res->{$key} = $v;
182            
183 0 0         if($v<=$threshold)
184             {
185 0           $res->{$key} = $v;
186 0 0         if($max_id < $index)
187             {
188 0           $max_id = $index;
189             }
190 0 0         if($v<$min)
191             {
192 0           $min = $v;
193             }
194             }
195 0           $index ++;
196             }
197            
198 0           foreach my $key (sort {$p->{$a} cmp $p->{$b}} keys %{$p} )
  0            
  0            
199             {
200 0 0         if($max_id>0)
201             {
202 0           $res->{$key} = $min;
203            
204 0           $max_id--;
205             }
206             }
207             }
208              
209             #mean read counts
210             sub raw_sum{
211 0     0 0   my($rarr_srbed,$chrom,$i) = @_;
212 0           my @arr_read;
213             # retrieve read count at window# 'i'.
214 0           my $r_mu = 0;
215 0           foreach my $b(@{$rarr_srbed}) {$r_mu += $b->get_bin_count($chrom,$i);}
  0            
  0            
216            
217 0           return $r_mu;
218             }
219              
220             sub raw_sum2{
221 0     0 0   my($rarr_srbed,$norm_ref) = @_;
222 0           my @arr_read;
223             # retrieve read count at window# 'i'.
224 0           my $r_mu = 0;
225 0           foreach my $i(0..@{$rarr_srbed}-1) {$r_mu += $rarr_srbed->[$i]/$norm_ref->[$i];}
  0            
  0            
226            
227 0           return $r_mu;
228             }
229             sub raw_sum_dir{
230 0     0 0   my($rarr_srbed,$chrom,$i) = @_;
231 0           my @arr_read;
232             # retrieve read count at window# 'i'.
233 0           my $r_mu = 0;
234 0           foreach my $b(@{$rarr_srbed})
  0            
235             {
236 0           my @count_both= $b->get_win_count_direction($chrom,$i);
237 0           $r_mu +=$count_both[0]+$count_both[1];
238             }
239            
240 0           return $r_mu;
241             }
242              
243             sub raw_mean{
244 0     0 0   my($rarr_srbed,$chrom,$i) = @_;
245 0           my @arr_read;
246             # retrieve read count at window# 'i'.
247 0           my $r_mu = 0;
248 0           foreach my $b(@{$rarr_srbed}) {$r_mu += $b->get_bin_count($chrom,$i);}
  0            
  0            
249            
250 0           return $r_mu/@{$rarr_srbed};
  0            
251             }
252             sub raw_mean2{
253 0     0 0   my($m1,$m2,$norm_ref) = @_;
254            
255 0           my $v = 0;
256 0           for my $i(@{$norm_ref})
  0            
257             {
258 0           $v +=1/$i;
259             }
260 0           $v = $v/@{$norm_ref};
  0            
261 0           return $v*($m1+$m2)/2;
262             }
263              
264             sub raw_mean_dir{
265 0     0 0   my($rarr_srbed,$chrom,$i) = @_;
266 0           my @arr_read;
267             # retrieve read count at window# 'i'.
268 0           my $r_mu = 0;
269 0           foreach my $b(@{$rarr_srbed}) {
  0            
270 0           my @count_both = $b->get_win_count_direction($chrom,$i);
271 0           $r_mu += $count_both[0] + $count_both[1];
272             #$b->get_bin_count($chrom,$i);
273             }
274            
275 0           return $r_mu/@{$rarr_srbed};
  0            
276             }
277              
278             # Calculate fold change given two values.
279             sub fold_change{
280 0     0 0   my($t,$c) = @_;
281 0 0 0       if($t < 0 or $c < 0){
282 0           warn "Negative value in fold change calculation!\n";
283 0           return 0;
284             }
285 0 0         if($t >= $c){
286 0 0         if($c == 0) {return $t+1;}
  0            
  0            
287             else {return $t/$c;}
288             }
289             else{
290 0 0         if($t == 0) {return -($c+1);}
  0            
  0            
291             else {return -$c/$t;}
292             }
293             }
294              
295             # Calculate Pearson's Chi-square test statistic.
296             sub chi_stat{
297 0     0 0   my($o,$n,$p) = @_;
298 0           return ($o - $n*$p)**2 / ($n*$p*(1-$p));
299             }
300              
301             # Function to read a list of names. Assuming the 1st column.
302             # Default: convert all names to upper case.
303             # assume the name is in the 1st column and there is no whitespace in names.
304             sub readnamelist{
305 0     0 0   my($file, $ra) = @_;
306 0           my $flag = 1;
307 0 0         $flag = $_[2] if @_ > 2; # read flag for upper case conversion.
308 0           @{$ra} = (); # deplete the list array first.
  0            
309 0 0         open HLIST, "<", $file or die "Open input file error: $!\n";
310 0           while(){
311 0           chomp;
312 0           my($name) = split;
313 0 0         $name = uc $name if $flag;
314 0           push @{$ra}, $name;
  0            
315             }
316 0           close HLIST;
317             }
318              
319             # Function to read a list of names with information in 1st and 2nd columns.
320             # Default: convert all names to upper case.
321             # assume there is no whitespace in names.
322             sub readnamewithinfolist{
323 0     0 0   my($file, $rh) = @_;
324 0           my $flag = 1;
325 0 0         $flag = $_[2] if @_ > 2; # read flag for upper case conversion.
326 0           %{$rh} = (); # deplete the list array first.
  0            
327 0 0         open HLIST, "<", $file or die "Open input file error: $!\n";
328 0           while(){
329 0           chomp;
330 0           my($name, $info) = split;
331 0 0         $name = uc $name if $flag;
332 0           $rh->{$name} = $info;
333             }
334 0           close HLIST;
335             }
336              
337             # Function to convert an array of names to hash table.
338             sub array2hash{
339 0     0 0   my($ra, $rh) = @_;
340 0           %{$rh} = (); # deplete the hash table first.
  0            
341 0           foreach(@{$ra}){
  0            
342 0           $rh->{$_} = 1;
343             }
344             }
345              
346             # Function to find the max element for an array.
347             sub max{
348 0 0   0 0   die "max function called for an empty array!\n" if @_ < 1;
349 0           my $m = $_[0];
350 0 0         for(my $i = 1; $i < @_; $i++) {$m = $_[$i] if $_[$i] > $m;}
  0            
351 0           return $m;
352             }
353              
354             # Function to find the min element for an array.
355             sub min{
356 0 0   0 0   die "min function called for an empty array!\n" if @_ < 1;
357 0           my $m = $_[0];
358 0 0         for(my $i = 1; $i < @_; $i++) {$m = $_[$i] if $_[$i] < $m;}
  0            
359 0           return $m;
360             }
361              
362             # Function to find the sum for an array.
363             sub sum{
364 0 0   0 0   die "sum function called for an empty array!\n" if @_ < 1;
365 0           my $s = 0;
366 0           foreach my $n(@_) {$s += $n;}
  0            
367 0           return $s;
368             }
369              
370             # Function to find the mean for an array.
371             sub mean{
372 0 0   0 0   die "mean function called for an empty array!\n" if @_ < 1;
373 0           my $m = 0;
374 0           my $c = 0;
375 0           foreach my $n(@_) {$m += $n; $c++;}
  0            
  0            
376 0           return $m / $c;
377             }
378              
379             # Function to perform trimmed mean. The array is passed as a reference.
380             sub mean_r{
381 0     0 0   my $r_v = shift;
382 0 0         if(@{$r_v} == 0){
  0            
383 0           warn "Empty array encountered! Return zero.\n";
384 0           return 0;
385             }
386 0           my($left_trim, $right_trim);
387 0 0         if(@_ > 0){ # left trim parameter.
388 0           $left_trim = shift;
389 0 0 0       unless($left_trim >= 0 and $left_trim < 0.5){
390 0           warn "Trimming parameter must be in: [0, 0.5). Reset to zero!\n";
391 0           $left_trim = 0;
392             }
393             }else{
394 0           $left_trim = 0;
395             }
396 0 0         if(@_ > 0){ # right trim parameter.
397 0           $right_trim = shift;
398 0 0 0       unless($right_trim >= 0 and $right_trim < 0.5){
399 0           warn "Trimming parameter must be in: [0, 0.5). Reset to zero!\n";
400 0           $right_trim = 0;
401             }
402             }else{
403 0           $right_trim = 0;
404             }
405 0           my @sorted_v = sort {$a <=> $b} @{$r_v};
  0            
  0            
406 0           my $left_mark = floor($left_trim * @sorted_v); # start position.
407 0           my $right_mark = @sorted_v - floor($right_trim * @sorted_v); # end +1 position.
408 0           my $m = 0;
409 0           for(my $i = $left_mark; $i < $right_mark; $i++){
410 0           $m += $sorted_v[$i];
411             }
412 0           $m /= $right_mark - $left_mark;
413 0           return $m;
414             }
415              
416             # Function to calculate the geometric mean for an array.
417             sub geomean{
418 0 0   0 0   die "geomean function called for an empty array!\n" if @_ < 1;
419 0           my $m = 0;
420 0           my $c = 0;
421 0           foreach my $n(@_) {
422 0 0         if($n == 0) {return 0;}
  0            
423 0           $m += log($n);
424 0           $c++;
425             }
426 0           return exp($m/$c);
427             }
428              
429             # Function to find the variance for an array.
430             sub var{
431 0 0   0 0   die "variance function called for an empty array!\n" if @_ < 1;
432 0           my $m = 0;
433 0           my $c = 0;
434 0           my $mean = mean(@_);
435 0           foreach my $n(@_) {$m += ($n-$mean)**2; $c++;}
  0            
  0            
436 0           return $m / ($c-1);
437             }
438              
439             # Function to find the median of a numeric array.
440             sub median{
441 0 0   0 0   die "median function called for an empty array!\n" if @_ < 1;
442 0           my @sorted = sort {$a <=> $b} @_;
  0            
443 0 0         if(@sorted % 2 == 1) {return $sorted[int(@sorted/2)];}
  0            
  0            
444             else {return ($sorted[int(@sorted/2)]+$sorted[int(@sorted/2)-1])/2;}
445             }
446              
447             # Function calculate mad: Median absolute deviation.
448             sub mad{
449 0     0 0   my $r_v = shift;
450 0 0         if(@{$r_v} == 0){ # reference to array.
  0            
451 0           return 0;
452             }
453 0           my($center, $constant);
454 0 0         if(@_ > 0){ # center.
455 0           $center = shift;
456             }else{
457 0           $center = median(@{$r_v});
  0            
458             }
459 0 0         if(@_ > 0){ # scale constant.
460 0           $constant = shift;
461 0 0         unless($constant > 0){
462 0           warn "Scale constant must be a positive number. Use default!\n";
463 0           $constant = 1.4826;
464             }
465             }else{
466 0           $constant = 1.4826;
467             }
468 0           my @dev;
469 0           foreach my $n(@{$r_v}){
  0            
470 0           push @dev, abs($n - $center);
471             }
472 0           return $constant * median(@dev);
473             }
474              
475             # logorithm base 2.
476             sub log2{
477 0     0 0   my $n = shift;
478 0 0         if($n == 0) {return -1*INFINITE;}
  0            
479 0           return log($n) / log(2);
480             }
481              
482             # logorithm base 10.
483             sub log10{
484 0     0 0   my $n = shift;
485 0 0         if($n == 0) {return -1*INFINITE;}
  0            
486 0           return log($n) / log(10);
487             }
488              
489             # A subroutine to read normalization constants for treatment and control.
490             # Syntax: treatment norm1 norm2...[normN]
491             # control norm1 norm2...[normN]
492             # whitespace should be used as field separator.
493             # only identifier 'treatment' and 'control' are recognized and they are case-sensitive.
494             # only the first two lines of the text file are considered and the rest are ignored.
495             sub read_norm2{
496 0     0 0   my($nf,$rt,$rc) = @_;
497 0 0         open HNORM, "<", $nf or die "Error in reading $nf:$!\n";
498 0           my @buf = ; # read in all lines into buffer.
499 0           chomp @buf;
500 0           my $tag_t = 0;
501 0           my $tag_c = 0;
502             # only deal with the first two lines.
503 0           my @line1 = split ' ', $buf[0];
504 0 0 0       if(@line1 > 0 and $line1[0] eq 'treatment'){
    0 0        
505 0           for(my $i = 1; $i < @line1; $i++) {push @{$rt}, $line1[$i];}
  0            
  0            
506 0           $tag_t = 1;
507             }
508             elsif(@line1 > 0 and $line1[0] eq 'control'){
509 0           for(my $i = 1; $i < @line1; $i++) {push @{$rc}, $line1[$i];}
  0            
  0            
510 0           $tag_c = 1;
511             }
512 0           my @line2 = split ' ', $buf[1];
513 0 0 0       if(@line2 > 0 and $line2[0] eq 'treatment'){
    0 0        
514 0           for(my $i = 1; $i < @line2; $i++) {push @{$rt}, $line2[$i];}
  0            
  0            
515 0           $tag_t = 1;
516             }
517             elsif(@line2 > 0 and $line2[0] eq 'control'){
518 0           for(my $i = 1; $i < @line2; $i++) {push @{$rc}, $line2[$i];}
  0            
  0            
519 0           $tag_c = 1;
520             }
521 0   0       return ($tag_t and $tag_c); # indicate whether both conditions are met.
522             }
523              
524              
525             # A subroutine to read cutoff thresholds for all samples.
526             # Syntax: treatment cutoff1 cutoff2...[cutoffN]
527             # control cutoff1 cutoff2...[cutoffN]
528             # whitespace should be used as field separator.
529             # only identifier 'treatment' and 'control' are recognized and they are case-sensitive.
530             # only the first two lines of the text file are considered and the rest are ignored.
531             sub read_cutoff{
532 0     0 0   my($nf,$tr_cut,$co_cut) = @_;
533 0 0         open HNORM, "<", $nf or die "Error in reading $nf:$!\n";
534 0           my @buf = ; # read in all lines into buffer.
535 0           chomp @buf;
536 0           my $tag_t = 0;
537 0           my $tag_c = 0;
538             # only deal with the first two lines.
539 0           my @line1 = split ' ', $buf[0];
540 0 0 0       if(@line1 > 0 and $line1[0] eq 'treatment'){
    0 0        
541 0           for(my $i = 1; $i < @line1; $i++) {push @{$tr_cut}, $line1[$i];}
  0            
  0            
542 0           $tag_t = 1;
543             }
544             elsif(@line1 > 0 and $line1[0] eq 'control'){
545 0           for(my $i = 1; $i < @line1; $i++) {push @{$co_cut}, $line1[$i];}
  0            
  0            
546 0           $tag_c = 1;
547             }
548 0           my @line2 = split ' ', $buf[1];
549 0 0 0       if(@line2 > 0 and $line2[0] eq 'treatment'){
    0 0        
550 0           for(my $i = 1; $i < @line2; $i++) {push @{$tr_cut}, $line2[$i];}
  0            
  0            
551 0           $tag_t = 1;
552             }
553             elsif(@line2 > 0 and $line2[0] eq 'control'){
554 0           for(my $i = 1; $i < @line2; $i++) {push @{$co_cut}, $line2[$i];}
  0            
  0            
555 0           $tag_c = 1;
556             }
557 0   0       return ($tag_t and $tag_c); # indicate whether both conditions are met.
558             }
559              
560             #check if the read counts of current bin is above the cutoff
561             sub isAboveCutoff
562             {
563 0     0 0   my ($rftr_read,$rfco_read,$rftr_cut,$rfco_cut)=@_;
564 0           my $len = @{$rftr_read};
  0            
565            
566 0           my $valid = 0;
567            
568 0           for my $i(0..@{$rftr_read}-1)
  0            
569             {
570 0 0         if($rftr_read->[$i] >= $rftr_cut->[$i])
571             {
572 0           $valid = 1;
573            
574             }
575             }
576 0 0         if($valid)
577             {
578 0           return $valid;
579             }
580 0           for my $i(0..@{$rfco_read}-1)
  0            
581             {
582 0 0         if($rfco_read->[$i] >= $rfco_cut->[$i])
583             {
584 0           return 1;
585             }
586             }
587            
588 0           return 0;
589             }
590              
591             # A subroutine to rescale normalization constants to the maximal one.
592             # max_n should be the maximum of both treatment and control.
593             # However, we may rescale treatment and control separately.
594             sub rescale_norm_max{
595 0     0 0   my($rn,$max_n) = @_;
596 0           my $i = 0;
597 0           foreach my $n(@{$rn}){
  0            
598 0 0         if($n <= 0){
  0            
599 0           warn "Normalization constant must be larger than zero! Force it to be zero.\n";
600 0           $rn->[$i++] = 0;
601             }
602             else {$rn->[$i++] = $max_n / $n;}
603             }
604             }
605              
606             # A subroutine to rescale cutoff constants.
607             sub rescale_cutoff{
608 0     0 0   my($r_cut,$r_norm) = @_;
609            
610 0           for my $i(0..@{$r_cut}-1){
  0            
611 0           $r_cut->[$i] = $r_cut->[$i] * $r_norm->[$i];
612             }
613             }
614              
615             # A subroutine to rescale normalization constants so that they sum up to one.
616             # sum_n should be the summation of both treatment and control.
617             # However, we may rescale treatment and control separately.
618             sub rescale_norm_sum1{
619 0     0 0   my($rn,$sum_n) = @_;
620 0           my $i = 0;
621 0           foreach my $n(@{$rn}){
  0            
622 0 0         if($n <= 0){
  0            
623 0           warn "Normalization constant must be larger than zero! Force it to be zero.\n";
624 0           $rn->[$i++] = 0;
625             }
626             else {$rn->[$i++] = $n / $sum_n;}
627             }
628             }
629              
630             # A subroutine to determine whether an array contains all zero elements.
631             sub is_all_zero{
632 0     0 0   my $ra = shift;
633 0 0         foreach (@{$ra}) {return 0 if $_ != 0;}
  0            
  0            
634 0           return 1;
635             }
636              
637             # Format a scalar or an array of numbers to specified decimal number.
638             sub fprecision{
639 0 0   0 0   return if @_ < 2;
640 0           my $n = shift;
641 0 0         if(@_ == 1) {return sprintf "%.$n" . "f", $_[0];}
  0            
642             else{
643 0           my @a;
644 0           foreach(@_) {push @a, sprintf "%.$n" . "f", $_;}
  0            
645 0           return @a;
646             }
647             }
648             #compute weight factor used in estimating variance
649             sub compute_beta{
650 0     0 0   my ($I,$v,$s,$S) = @_;
651 0           my $beta = (2*($I-1)/($v+2))*(1/$I+$s*$s/$S);
652 0 0         if($beta>1)
  0            
653             {return 1;}
654 0           return $beta;
655             }
656             #adjust variance
657             sub adj_var{
658 0     0 0   my ($b,$rep_s,$neib_s) = @_;
659            
660 0           return (1-$b)*$rep_s+$b*$neib_s;
661             }
662             #compute negative binomial statistic score (estimate variance using the variances of its neighbors)
663             sub nb_stat{
664 0     0 0   my ($ref_q,$q_size,$start_pos,$num_rep,$epsilon,$step,$cur_pos) = @_;
665            
666             #my $cur_pos = $q_size/2;
667 0 0         if($start_pos ==0 )
668             {
669 0           $start_pos = $q_size;
670 0           $cur_pos = 0;
671             }
672 0 0         if($start_pos ==$cur_pos-1)
673             {
674 0           $cur_pos =0;
675             }
676 0           for(my $l=$start_pos;$l>=$cur_pos;$l--) #process all upstream windows of the window pointed by $cur_pos
677             {
678            
679 0           my %cur_stat= %{$ref_q->[$l]};
  0            
680             # do statistics on normalized tr_read and co_read arrays if pass cutoff.
681            
682 0           my $num_neighbor = 0;
683             #variance of neighbors
684 0           for(my $k=$q_size;$k>=0;)
685             {
686 0           my %stat = %{$ref_q->[$k]};
  0            
687 0           $cur_stat{tr_neighbor_var} += $stat{tr_replicate_var};
688 0           $cur_stat{co_neighbor_var} += $stat{co_replicate_var};
689 0           $k = $k - $step;
690 0           $num_neighbor++;
691             }
692            
693 0           $cur_stat{tr_neighbor_var} = $cur_stat{tr_neighbor_var}/$num_neighbor;
694 0           $cur_stat{co_neighbor_var} = $cur_stat{co_neighbor_var}/$num_neighbor;
695            
696 0           for(my $k=$q_size;$k>=0;) #variance diff
697             {
698 0           my %stat = %{$ref_q->[$k]};
  0            
699 0           $cur_stat{tr_var_diff} += ($stat{tr_replicate_var}-$cur_stat{tr_neighbor_var})**2;
700 0           $cur_stat{co_var_diff} += ($stat{co_replicate_var}-$cur_stat{co_neighbor_var})**2;
701 0           $k = $k - $step;
702             }
703            
704 0           my $tr_beta = compute_beta($num_neighbor,$num_rep,$cur_stat{tr_neighbor_var},$cur_stat{tr_var_diff});
705 0           my $co_beta = compute_beta($num_neighbor,$num_rep,$cur_stat{co_neighbor_var},$cur_stat{co_var_diff});
706             # print "$tr_beta\t$co_beta\n";
707            
708 0           my $tr_var = adj_var($tr_beta,$cur_stat{tr_replicate_var},$cur_stat{tr_neighbor_var});
709 0           my $co_var = adj_var($co_beta,$cur_stat{co_replicate_var},$cur_stat{co_neighbor_var});
710            
711 0           my $tr_mean = mean(@{$cur_stat{tr_read}});
  0            
712 0           my $co_mean = mean(@{$cur_stat{co_read}});
  0            
713            
714 0 0         if($tr_mean > $co_mean) {$cur_stat{dirn}='Up';}
  0            
715 0 0         if($tr_mean < $co_mean) {$cur_stat{dirn}='Down';}
  0            
716 0 0         if($tr_mean ==$co_mean) {$cur_stat{dirn}='--';}
  0            
717            
718 0 0         if($tr_var <$tr_mean)
719             {
720 0           $tr_var = $tr_mean + $epsilon;
721             }
722 0 0         if($co_var < $co_mean)
723             {
724 0           $co_var = $co_mean + $epsilon;
725             }
726            
727             # print "$cur_stat{tr_mean}\t$tr_var\t$cur_stat{co_mean}\t$co_var\n";
728 0           $tr_var = $cur_stat{tr_replicate_var};
729 0           $co_var = $cur_stat{co_replicate_var};
730 0           my $pval = nb_pval($cur_stat{tr_mean},$cur_stat{co_mean},$tr_mean,$tr_var,$co_mean,$co_var,$epsilon);
731            
732 0           $cur_stat{score} = $pval;
733             # print "$cur_stat{tr_mean}\t$cur_stat{tr_replicate_var}\t$tr_var\t$cur_stat{co_mean}\t$cur_stat{co_replicate_var}\t$co_var\n";
734             # print "pval = $pval\n";
735            
736 0           $ref_q->[$l] = \%cur_stat;
737            
738             }
739             }
740              
741             sub nb_pval_v2{
742 0     0 0   my ($ka,$miu1,$var1,$eps) = @_;
743            
744            
745 0           my @rp1 = ();
746 0           nb_r_p($miu1,$var1,\@rp1,$eps);
747            
748 0           my $r1 = $rp1[0];
749 0           my $p1 = $rp1[1];
750            
751 0 0         if($ka <= $miu1){
752 0           return &Math::CDF::pnbinom($ka,$r1,$p1);
753             }
754             else
755             {
756 0           return 1 - &Math::CDF::pnbinom($ka,$r1,$p1);
757             }
758             }
759              
760             1;
761             __END__