File Coverage

GO/Model/GraphIterator.pm
Criterion Covered Total %
statement 110 182 60.4
branch 26 64 40.6
condition 9 27 33.3
subroutine 17 24 70.8
pod 4 5 80.0
total 166 302 54.9


line stmt bran cond sub pod time code
1             # $Id: GraphIterator.pm,v 1.11 2008/01/22 23:54:46 cmungall Exp $
2             #
3             # This GO module is maintained by Chris Mungall
4             #
5             # see also - http://www.geneontology.org
6             # - http://www.godatabase.org/dev
7             #
8             # You may distribute this module under the same terms as perl itself
9              
10              
11             package GO::Model::GraphIterator;
12              
13             =head1 NAME
14              
15             GO::Model::GraphIterator;
16              
17             =head1 SYNOPSIS
18              
19             $it = $graph->create_iterator;
20             # returns a GO::Model::GraphIterator object
21              
22             while (my $ni = $it->next_node_instance) {
23             $depth = $ni->depth;
24             $term = $ni->term;
25             $reltype = $ni->parent_rel->type;
26             printf
27             "%s %8s Term = %s (%s) // depth=%d\n",
28             "----" x $depth,
29             $reltype,
30             $term->name,
31             $term->public_acc,
32             $depth;
33             }
34              
35             =head1 DESCRIPTION
36              
37             =head1 SEE ALSO
38              
39             L
40              
41             L
42              
43             =cut
44              
45              
46 14     14   80 use Carp;
  14         537  
  14         1248  
47 14     14   77 use strict;
  14         26  
  14         384  
48 14     14   68 use Exporter;
  14         23  
  14         506  
49 14     14   71 use GO::Utils qw(rearrange);
  14         41  
  14         570  
50 14     14   76 use GO::Model::Graph;
  14         105  
  14         314  
51 14     14   5904 use GO::Model::GraphNodeInstance;
  14         126  
  14         594  
52 14     14   84 use FileHandle;
  14         27  
  14         62  
53 14     14   5131 use Exporter;
  14         37  
  14         474  
54 14     14   121 use Data::Dumper;
  14         61  
  14         849  
55 14     14   68 use vars qw(@EXPORT_OK %EXPORT_TAGS);
  14         23  
  14         701  
56              
57 14     14   72 use base qw(GO::Model::Root Exporter);
  14         22  
  14         30920  
58              
59             sub _valid_params {
60 7331     7331   30541 return qw(graph acc order sort_by sort_by_list noderefs direction no_duplicates reltype_filter visited arcs_visited compact subset_h);
61             }
62              
63             =head2 order
64              
65             Usage - $graphiter->order("breadth");
66             Returns - string
67             Args - string
68              
69             gets/sets traversal order; breadth or depth; default is depth
70              
71             =cut
72              
73             =head2 direction
74              
75             Usage - $graphiter->direction("up");
76             Returns - string
77             Args - string
78              
79             gets/sets direction; default is "down"
80              
81             =cut
82              
83             =head2 compact
84              
85             Usage - $graphiter->compact(1);
86             Returns - bool
87             Args - bool
88              
89             set this if you dont want relationships to be traversed twice;
90             this gives a more compact tree representation of the graph
91              
92             =cut
93              
94             =head2 reltype_filter
95              
96             Usage - $graphiter->reltype_filter(qw(is_a part_of))
97             Returns - array
98             Args - array
99              
100             by default, all relationship types are treated as transitive, and will
101             be traversed by the iterator
102              
103             sometimes you dont want to traverse all relations, even if they are
104             transitive. For example, when answering the query "find all genes
105             expressed in the embryo" you way want subtypes of embryo and parts of
106             the embryo but not things that develop_from the embryo.
107              
108             For more details, see
109             L
110              
111             =cut
112              
113             sub _initialize {
114 8     8   17 my $self = shift;
115 8         14 my $acc;
116 8 50       32 if (!ref($_[0])) {
117 0         0 $acc = shift;
118             }
119 8         49 $self->SUPER::_initialize(@_);
120 8 50       65 $acc = $self->acc unless $acc;
121 8         37 $self->reset_cursor($acc);
122             }
123              
124              
125             =head2 reset_cursor
126              
127             Usage -
128             Returns -
129             Args -
130              
131             =cut
132              
133             sub reset_cursor {
134 9     9 1 16 my $self = shift;
135 9         13 my $acc = shift;
136              
137 9         61 $self->visited({});
138              
139 9         65 $self->arcs_visited({});
140              
141 9         18 my $terms;
142 9 100       28 if ($acc) {
143 2   33     11 $terms = [$self->graph->get_term($acc) || confess("$acc not in graph")];
144             }
145             else {
146 7 50 33     49 if (!$self->direction || $self->direction ne "up") {
147 7         39 $terms = $self->graph->get_top_nodes;
148             # foreach (@$terms) {
149             # printf "TOP: %s\n", $_->acc;
150             # }
151             }
152             else {
153 0         0 $terms = $self->graph->get_leaf_nodes;
154             }
155             }
156              
157 9   50     111 my $sort_by = $self->sort_by || "alphabetical";
158 9   50     83 my $sort_by_list = $self->sort_by_list || [];
159             # print "
sort_by_list has ".scalar(@$sort_by_list)." elements , number of terms to sort = ".scalar(@$terms)."
\n"
160             # if ($sort_by eq 'pos_in_list');
161 7     7   23 my %fh =
162             (
163             "alphabetical" => sub {lc($a->name) cmp lc($b->name)},
164 0     0   0 "pos_in_list" => sub {_sortby_pos_in_list($sort_by_list, $a, $b)}
165 9         105 );
166 9         22 my $sortf = $fh{$sort_by};
167 9 50       36 confess("Dont know $sort_by") unless $sortf;
168 9         37 my @sorted_terms = sort $sortf @$terms;
169              
170 16         117 my @noderefs =
171             map {
172 9         22 GO::Model::GraphNodeInstance->new({term=>$_, depth=>0})
173             } @sorted_terms;
174 9         1126 $self->noderefs(\@noderefs);
175             }
176              
177              
178             =head2 next_node
179              
180             Usage -
181             Returns - GO::Model::Term
182             Args -
183              
184             =cut
185              
186             sub next_node {
187 0     0 1 0 my $self = shift;
188 0         0 my $ni = $self->next_node_instance;
189 0 0       0 return $ni ? $ni->term : undef;
190             }
191              
192              
193             =head2 next_node_instance
194              
195             Usage -
196             Returns - GO::Model::GraphNodeInstance
197             Args -
198              
199             =cut
200              
201             sub next_node_instance {
202 499     499 1 32272 my $self = shift;
203 499 50       2740 if (!$self->noderefs) {
204 0         0 $self->reset_cursor;
205             }
206 499         3589 my $noderefs = $self->noderefs;
207 499 100       1854 if (!@$noderefs) {
208 8         52 return;
209             }
210 491   50     2019 my $order = $self->order || "depth";
211 491         1425 my $noderef = shift @$noderefs;
212 491         2220 my $term = $noderef->term;
213 491         2422 my $depth = $noderef->depth;
214 491         1322 my @child_relns = ();
215 491 100 66     2411 my $dir =
216             (!$self->direction || $self->direction ne "up") ? "down" : "up";
217              
218             # default is to traverse a distance of 1 in the DAG
219             # however, if subset_h is set, we want to traverse the
220             # transitive distance to the next node in the specified subset
221 491         2462 my $subset_h = $self->subset_h;
222 491         1713 my @accs = ($term->acc); # current IDs
223              
224             # iterate to next node - usually just 1 iteration, unless subset_h is set
225 491         1222 while (@accs) {
226 491         638 my @this_child_relns = ();
227 491         706 my $acc = shift @accs;
228 491 100       1047 if ($dir eq "down") {
    50          
229 486         2024 @this_child_relns =
230 486         504 @{$self->graph->get_child_relationships($acc)};
231 486 50       1647 if ($subset_h) {
232             @this_child_relns =
233             grep {
234 0 0       0 if ($subset_h->{$_->acc2}) {
  0         0  
235 0         0 $_->acc1($term->acc);
236 0         0 1;
237             }
238             else {
239 0         0 push(@accs, $_->acc2);
240 0         0 0;
241             }
242             } @this_child_relns;
243             }
244             }
245             elsif ($dir eq "up") {
246 5         24 @this_child_relns =
247 5         7 @{$self->graph->get_parent_relationships($acc)};
248 5 50       19 if ($subset_h) {
249 0         0 @this_child_relns =
250             grep {
251 0         0 my $keep;
252 0 0       0 if ($subset_h->{$_->acc1}) {
253 0         0 $_->acc2($term->acc);
254 0         0 $keep=1;
255             }
256             else {
257 0         0 push(@accs, $_->acc1);
258 0         0 $keep=0;
259             }
260 0         0 $keep;
261             } @this_child_relns;
262             }
263             }
264             else {
265 0         0 die $dir;
266             }
267 491         1724 push(@child_relns, @this_child_relns);
268             }
269            
270 491 50       2437 if ($self->reltype_filter) {
271 0         0 my %filh = ();
272 0         0 my $fs = $self->reltype_filter;
273 0 0       0 $fs = [$fs] unless ref($fs);
274 0         0 %filh = map {lc($_)=>1} @$fs;
  0         0  
275 0         0 @child_relns =
276 0         0 grep { $filh{lc($_->type)} } @child_relns;
277             }
278              
279 491 100       23656 if ($self->compact) {
280 373         1390 @child_relns =
281 342         467 grep { !$self->arcs_visited->{$_->as_str} } @child_relns;
282             }
283              
284 491         1414 my @new = ();
285              
286 491         1047 foreach (@child_relns) {
287 476         8877 $self->arcs_visited->{$_->as_str} = 1;
288 476 100       4022 my $t = $self->graph->get_term($dir ne "up" ? $_->acc2 : $_->acc1);
289 476 50       1446 if ($t) {
290 476         1739 my $h =
291             {
292             term=>$t,
293             depth=>($depth+1),
294             parent_rel=>$_,
295             };
296 476         3534 push(@new,
297             GO::Model::GraphNodeInstance->new($h));
298             }
299             }
300            
301 491   50     2519 my $sort_by = $self->sort_by || "alphabetical";
302 491   50     2596 my $sort_by_list = $self->sort_by_list || [];
303              
304 533     533   2663 my %fh =
305             (
306             "alphabetical" => sub {lc($a->term->name) cmp lc($b->term->name)},
307 0     0   0 "pos_in_list" => sub {_sortby_pos_in_list($sort_by_list, $a->term, $b->term)}
308 491         6145 );
309 491         1050 my $sortf = $fh{$sort_by};
310 491 50       1042 confess("Dont know $sort_by") unless $sortf;
311              
312 491         1149 @new = sort $sortf @new;
313              
314 491         2186 my $visited = $self->visited;
315              
316 491 100       2513 if ($self->no_duplicates) {
317             # don't visit nodes twice
318 5         22 @new = grep {!$visited->{$_->term->acc}} @new;
  4         22  
319             }
320 491         1273 foreach (@new) {
321 476         12621 $visited->{$_->term->acc} = 1;
322             }
323              
324 491 50       991 if ($order eq "breadth") {
325 0         0 push(@$noderefs, @new);
326             }
327             else {
328             # depth first:
329 491         1096 splice(@$noderefs, 0, 0, @new);
330             }
331 491         5327 return $noderef;
332             }
333              
334              
335             =head2 flatten
336              
337             Usage -
338             Returns -
339             Args -
340              
341             =cut
342              
343             sub flatten {
344 0     0 1   my $self = shift;
345 0           my ($bracket, $fmt) =
346             rearrange([qw(bracket fmt)], @_);
347              
348 0           my $str = "";
349 0   0       $fmt ||= "%s";
350 0           my $depth = 0;
351              
352 0 0         my $ob = $bracket ? substr($bracket, 0, 1) : "(";
353 0 0         my $cb = $bracket ? substr($bracket, -1, 1) : ")";
354              
355             sub diffchr {
356 0     0 0   my ($dd, $ob, $cb) = @_;
357 0           my $ch;
358 0 0         if ($dd < 0) {
    0          
359 0           $ch = "$cb" x -$dd;
360             }
361             elsif ($dd > 0) {
362 0           $ch = "$ob" x $dd;
363             }
364             else {
365 0           $ch = "";
366             }
367             }
368              
369 0           while (my $ni = $self->next_node_instance) {
370 0           my $dd = $ni->depth - $depth;
371              
372 0           my $ch = diffchr($dd, $ob, $cb);
373 0           $depth = $ni->depth;
374 0           $str .=
375             sprintf(" $ch $fmt",
376             $ni->term->public_acc,
377             $ni->term->name,
378             $ni->term->definition);
379             }
380              
381 0           $str .= diffchr(-$depth, $ob, $cb);
382 0           return $str;
383             }
384              
385              
386             =head2 _sortby_pos_in_list
387              
388             Careful, this sort function work on Term objects, not GraphNodeInstance
389             objects. Comparison is done by the name of the term.
390              
391             =cut
392              
393             sub _sortby_pos_in_list
394             {
395 0     0     my ($t_list, $t_a, $t_b) = @_;
396             # print "
_sortby called (".join(",",map {$_->name} @$t_list).") // ".$t_a->name." // ".$t_b->name."
\n";
397 0           my $inf = 100000000;
398              
399             # First see which is first in list
400 0           my $a_pos = _term_pos_in_list($t_list, $t_a);
401 0           my $b_pos = _term_pos_in_list($t_list, $t_b);
402              
403             # If one is bigger than the other, return the bigger one.
404 0           my $res = 0;
405 0           my $name_cmp = lc($t_a->name) cmp lc($t_b->name);
406 0 0 0       if (($a_pos >= 0) && ($b_pos >= 0))
    0 0        
407             {
408             # Both are in list
409 0 0         if ($a_pos != $b_pos) {
410 0           $res = ($a_pos <=> $b_pos);
411             } else {
412 0           $res = $name_cmp;
413             }
414             }
415             elsif (($a_pos < 0) && ($b_pos < 0))
416             {
417             # Neither are in the list
418 0           $res = $name_cmp;
419             }
420             else
421             {
422             # One is in the list and the other isn't
423 0 0         $res = ($a_pos >= 0) ? 1 : -1;
424             }
425              
426 0           return $res;
427             }
428              
429             sub _term_pos_in_list
430             {
431 0     0     my ($t_list, $t) = @_;
432              
433             # First see which is first in list
434 0           my $out = -1;
435 0           my $num_terms = scalar(@$t_list);
436 0           for (my $i = 0; $i < $num_terms; $i++) {
437 0           my $cur_t = @{$t_list}[$i];
  0            
438 0 0         return $i if (lc($cur_t->name) eq lc($t->name));
439             }
440              
441 0           return $out;
442             }
443              
444              
445              
446              
447             1;