File Coverage

blib/lib/obogaf/parser.pm
Criterion Covered Total %
statement 192 192 100.0
branch 83 86 96.5
condition 3 3 100.0
subroutine 11 11 100.0
pod 6 6 100.0
total 295 298 98.9


line stmt bran cond sub pod time code
1             package obogaf::parser;
2              
3             require 5.006;
4             our $VERSION= '1.271';
5             $VERSION= eval $VERSION;
6              
7 6     6   513704 use strict;
  6         65  
  6         173  
8 6     6   31 use warnings;
  6         14  
  6         156  
9 6     6   4918 use Graph;
  6         692525  
  6         252  
10 6     6   2903 use IO::File;
  6         53149  
  6         696  
11 6     6   2920 use PerlIO::gzip;
  6         3457  
  6         15376  
12              
13             sub build_edges{
14 7     7 1 18214 my ($obofile)= @_;
15 7         21 my ($namespace, $idname, $isname, $pofname, $source, $destination, $pof, $res);
16 7 100       46 if($obofile=~/.obo$/){ open FH, "<", "$obofile" or die "cannot open $obofile. $!.\n"; } else { die "cannot open $obofile. The extension must be obo.\n"; }
  6 100       259  
  1         8  
17 5         117 while(){
18 229         310 chomp;
19 229 100       509 next if $_=~/^\s*$/;
20 219 100       795 if($_=~/^namespace:\s+(\D+)/){
    100          
    100          
    100          
    100          
21 4         25 $namespace=$1;
22             }elsif($_=~/^name:\s+(.+)/){
23 5         28 $idname=$1;
24             }elsif($_=~/^id:\s+(\D+\d+)/){
25 5         27 $destination=$1;
26             }elsif($_=~/^is_a:\s+(\D+\d+)/){
27 5         11 $source=$1;
28 5         28 ($isname)= ($_=~/!\s+(.+)/);
29 5 100       19 if(defined $namespace){
30 4         31 $res .= "$namespace\t$source\t$destination\t$isname\t$idname\tis-a\n";
31             }else{
32 1         8 $res .= "$source\t$destination\t$isname\t$idname\tis-a\n";
33             }
34             }elsif($_=~/^relationship: part_of\s+(\D+\d+)/){
35 5         40 $pof=$1;
36 5         41 ($pofname)= ($_=~/!\s+(.+)/);
37 5 100       35 if(defined $namespace){
38 4         32 $res .= "$namespace\t$pof\t$destination\t$pofname\t$idname\tpart-of\n";
39             }else{
40 1         8 $res .= "$pof\t$destination\t$pofname\t$idname\tpart-of\n";
41             }
42             }
43             }
44 5         50 close FH;
45 5         30 return \$res;
46             }
47              
48             sub build_subonto{
49 5     5 1 19520 my ($edgesfile, $namespace)= @_;
50 5         12 my ($res, %checker);
51 5 100       195 open FH, "<", $edgesfile or die "cannot open $edgesfile. $!.\n";
52 4         78 while(){
53 9 100       132 next if $_=~/^[!,#]|^\s*$/;
54 8         45 my @vals= split(/\t/, $_);
55 8         25 $checker{$vals[0]}=1;
56 8 100       28 if($vals[0] eq $namespace){ $res .= join("\t", @vals[1..$#vals]); }
  6         59  
57             }
58 4         39 close FH;
59 4 100       18 unless(exists($checker{$namespace})){die "$edgesfile does not include $namespace or $namespace is not in the first column of $edgesfile.\n";}
  1         18  
60 3         20 return \$res;
61             }
62              
63             sub make_stat{
64 5     5 1 2364 my ($edgesfile, $parentIndex, $childIndex)= @_;
65 5         13 my (%indeg, %outdeg, %deg, $ed, $nd, $mindeg, $maxdeg, $medeg, $avgdeg, $den, $scc, $resdeg, $stat, $res);
66             ## create graph
67 5         26 my $g= Graph->new(directed => 1);
68 5 100       1428 open FH, "<", $edgesfile or die "cannot open $edgesfile. $!.\n";
69 4         75 while(){
70 7         429 chomp;
71 7         33 my @vals= split(/\t/,$_);
72 7         31 $g->add_edge($vals[$parentIndex], $vals[$childIndex]);
73             }
74 4         421 close FH;
75             ## compute indegree/outdegree/degree
76 4         21 my @V= $g->vertices;
77 4         226 foreach my $nd (@V){
78 11         28 my $i= $g->in_degree($nd);
79 11         1252 my $o= $g->out_degree($nd);
80 11         1202 my $d= $i+$o;
81 11         21 $indeg{$nd}=$i;
82 11         16 $outdeg{$nd}=$o;
83 11         20 $deg{$nd}=$d;
84             }
85 4 50       20 foreach my $node (sort{$deg{$b}<=>$deg{$a} or ($a cmp $b)} keys %deg){ $resdeg .= "$node\t$deg{$node}\t$indeg{$node}\t$outdeg{$node}\n"; }
  9         32  
  11         39  
86             ## compute: median/max/min degree
87 4         13 my @sortdeg= sort{$a<=>$b} values (%deg);
  10         21  
88 4         9 my $len= $#sortdeg+1;
89 4         13 my $mid = int $len/2;
90 4 100       13 if($len % 2){ $medeg = $sortdeg[$mid]; }else{ $medeg = ( $sortdeg[$mid-1] + $sortdeg[$mid] ) / 2; }
  3         5  
  1         4  
91 4         31 $medeg= sprintf("%.4f", $medeg);
92 4         9 $mindeg= $sortdeg[0];
93 4         7 $maxdeg= $sortdeg[$#sortdeg];
94             ## compute number of nodes and edges
95 4         13 $ed= $g->edges;
96 4         150 $nd= $g->vertices;
97             ## compute average degree and density
98 4         169 $avgdeg= $ed/$nd;
99 4         10 $den= $ed / ( $nd * ($nd -1) );
100 4         20 $avgdeg= sprintf("%.4f", $avgdeg);
101 4         13 $den= sprintf("%.4e", $den);
102             ## return stat
103 4         15 $stat .= "nodes: $nd\nedges: $ed\nmax degree: $maxdeg\nmin degree: $mindeg\n";
104 4         11 $stat .= "median degree: $medeg\naverage degree: $avgdeg\ndensity: $den\n";
105 4         12 $res= "#oboterm degree indegree outdegree\n".$resdeg."\n"."~summary stat~\n".$stat;
106 4         64 return $res;
107             }
108              
109             sub get_parents_or_children_list{
110 10     10 1 19132 my ($edgesfile, $parentIndex, $childIndex, $chdORpar)= @_;
111 10         19 my (%nodelist);
112 10 100 100     43 if($chdORpar ne "parents" && $chdORpar ne "children"){ die "$chdORpar can be 'parents' or 'children'.\n";}
  2         17  
113 8 100       290 open FH, "<", $edgesfile or die "cannot open $edgesfile. $!.\n";
114 6         113 while(){
115 12         28 chomp;
116 12         48 my @vals= split(/\t/,$_);
117 12 100       26 if($chdORpar eq "parents"){
118 6         48 $nodelist{$vals[$childIndex]} .= $vals[$parentIndex]."|";
119             }else{
120 6         48 $nodelist{$vals[$parentIndex]} .= $vals[$childIndex]."|";
121             }
122             }
123 6         55 close FH;
124 6         27 foreach my $term (keys %nodelist){ chop $nodelist{$term}; }
  9         18  
125 6         37 return \%nodelist;
126             }
127              
128             sub gene2biofun{
129 6     6 1 18922 my ($annfile, $geneIndex, $classIndex)= @_;
130 6         14 my (%gene2biofun, @genes, @biofun, $stat)= ();
131 6         18 my ($sample, $oboterm)= (0)x2;
132 6 100       26 if ($annfile =~ /.gz$/){
133 2 100       118 open FH, "<:gzip", $annfile or die "cannot open $annfile. $!.\n";
134             }else{
135 4 100       160 open FH, "<", "$annfile" or die "cannot open $annfile. $!.\n";
136             }
137 4         142 while(){
138 27 100       474 next if $_=~/^[!,#]|^\s*$/;
139 24         35 chomp;
140 24         103 my @vals=split(/\t/,$_);
141 24         48 push(@genes, $vals[$geneIndex]);
142 24         36 push(@biofun, $vals[$classIndex]);
143 24         118 $gene2biofun{$vals[$geneIndex]} .= $vals[$classIndex]."|";
144             }
145 4         41 close FH;
146 4         19 foreach my $gene (keys %gene2biofun){ chop $gene2biofun{$gene}; }
  8         18  
147 4         20 my %seen=();
148 4         10 my @uniqgenes= grep{!$seen{$_}++} @genes;
  24         59  
149 4         9 $sample= scalar(@uniqgenes);
150 4         10 undef %seen;
151 4         7 my @uniqpbiofun= grep{!$seen{$_}++} @biofun;
  24         61  
152 4         8 $oboterm= scalar(@uniqpbiofun);
153 4         15 $stat .= "genes: $sample\nontology terms: $oboterm\n";
154 4         31 return (\%gene2biofun, \$stat);
155             }
156              
157             sub map_OBOterm_between_release{
158 9     9 1 21558 my ($obofile, $annfile, $classIndex)= @_;
159 9         20 my (%altid, %oldclass, %old2new, $header, $id, $fln, $pair, $stat, $pstat);
160 9         22 my ($alt, $classes, $seen, $unseen)= (0)x4;
161             ## step 0: pairing altid_2_id (key: alt_id)
162 9 100       49 if($obofile=~/.obo$/){ open FH, "<", "$obofile" or die "cannot open $obofile. $!.\n"; } else { die "cannot open $obofile. The extension must be obo.\n"; }
  8 100       281  
  1         9  
163 7         123 while (){
164 322         430 chomp;
165 322 100       699 next if $_=~/^\s*$/;
166 308 100       506 if($_=~/^id:\s+(\D+\d+)/){ $id=$1; }
  7         21  
167 308 100       721 if($_=~/^alt_id:\s+(\D+\d+)/){ $altid{$1}=$id; }
  49         184  
168             }
169 7         71 close FH;
170 7         22 $alt= keys(%altid);
171             # step 1: storing old ontology terms in a hash
172 7 100       28 if ($annfile =~ /.gz$/){
173 2 100       108 open FH, "<:gzip", $annfile or die "cannot open $annfile. $!.\n";
174             }else{
175 5 100       166 open FH, "<", "$annfile" or die "cannot open $annfile. $!.\n";
176             }
177 5         122 while(){
178 28         55 chomp;
179 28 100       419 if($_=~/^[!,#]|^\s*$/){ $header .= "$_\n"; }
  2         6  
180 28 100       402 next if $_=~/^[!,#]|^\s*$/;
181 26         103 my @vals=split(/\t/,$_);
182 26         139 $oldclass{$vals[$classIndex]}=$vals[$classIndex];
183             }
184 5         45 close FH;
185 5         14 $classes= keys(%oldclass);
186             ## step 2: mapping old GO terms to the new one using *alt_id* as key
187 5         9 my $tmp= "";
188 5         28 foreach my $k (sort{$a cmp $b} keys(%altid)){
  69         95  
189 35 100       59 if($oldclass{$k}){
190 8         18 $old2new{$k}=$altid{$oldclass{$k}}; ## pairing
191 8         11 $seen++;
192 8         12 $tmp= $altid{$oldclass{$k}};
193             }else{
194 27         38 $tmp= "unseen";
195 27         34 $unseen++;
196             }
197 35 100       67 if($tmp ne "unseen"){
198 8         20 $pair .= "$k\t$altid{$oldclass{$k}}\n";
199             }
200             }
201             ## step 3: substitute ALT-ID with the updated ID, then the annotation file is returned.
202 5 100       65 if ($annfile =~ /.gz$/){
203 1 50       44 open FH, "<:gzip", $annfile or die "cannot open $annfile. $!.\n";
204             }else{
205 4 50       135 open FH, "<", "$annfile" or die "cannot open $annfile. $!.\n";
206             }
207 5         71 while(){
208 28         69 chomp;
209 28 100       432 next if $_=~/^[!,#]|^\s*$/;
210 26         136 my @vals= split(/\t/, $_);
211 26         48 my $oboterm= $vals[$classIndex];
212 26 100       72 if($old2new{$oboterm}){
213 16         27 $oboterm= $old2new{$oboterm};
214 16         143 $_=~ s/$vals[$classIndex]/$oboterm/g;
215 16         91 $fln .= "$_\n";
216             }else{
217 10         109 $fln .= "$_\n";
218             }
219             }
220 5         48 close FH;
221 5 100       18 if(defined $header){$fln = $header.$fln;}
  1         4  
222             ## print mapping stat
223 5         20 $stat .= "Tot. ontology terms:\t$classes\nTot. altID:\t$alt\nTot. altID seen:\t$seen\nTot. altID unseen:\t$unseen\n";
224 5 100       11 unless(not defined $pair){
225 4         13 $pstat .= "#alt-id id\n$pair\n$stat";
226 4         39 return (\$fln, \$pstat);
227             }
228 1         10 return (\$fln, \$stat);
229             }
230              
231             1;
232              
233             __END__