File Coverage

Bio/Search/Result/BlastResult.pm
Criterion Covered Total %
statement 78 112 69.6
branch 19 38 50.0
condition 1 5 20.0
subroutine 14 21 66.6
pod 17 17 100.0
total 129 193 66.8


line stmt bran cond sub pod time code
1             #
2             # BioPerl module for Bio::Search::Result::BlastResult
3             #
4             # Please direct questions and support issues to
5             #
6             # Cared for by Steve Chervitz
7             #
8             # Copyright Steve Chervitz
9             #
10             # You may distribute this module under the same terms as perl itself
11              
12             # POD documentation - main docs before the code
13              
14             =head1 NAME
15              
16             Bio::Search::Result::BlastResult - Blast-specific subclass of Bio::Search::Result::GenericResult
17              
18             =head1 SYNOPSIS
19              
20             # Working with iterations (PSI-BLAST results)
21              
22             $result->next_iteration();
23             $result->num_iterations();
24             $result->iteration();
25             $result->iterations();
26              
27             # See Bio::Search::Result::GenericResult for information about working with Results.
28              
29             # See L
30             # for details about working with iterations.
31              
32             # TODO:
33             # * Show how to configure a SearchIO stream so that it generates
34             # BlastResult objects.
35              
36              
37             =head1 DESCRIPTION
38              
39             This object is a subclass of Bio::Search::Result::GenericResult
40             and provides some operations that facilitate working with BLAST
41             and PSI-BLAST results.
42              
43             For general information about working with Results, see
44             Bio::Search::Result::GenericResult.
45              
46             =head1 FEEDBACK
47              
48             =head2 Mailing Lists
49              
50             User feedback is an integral part of the evolution of this and other
51             Bioperl modules. Send your comments and suggestions preferably to
52             the Bioperl mailing list. Your participation is much appreciated.
53              
54             bioperl-l@bioperl.org - General discussion
55             http://bioperl.org/wiki/Mailing_lists - About the mailing lists
56              
57             =head2 Support
58              
59             Please direct usage questions or support issues to the mailing list:
60              
61             I
62              
63             rather than to the module maintainer directly. Many experienced and
64             reponsive experts will be able look at the problem and quickly
65             address it. Please include a thorough description of the problem
66             with code and data examples if at all possible.
67              
68             =head2 Reporting Bugs
69              
70             Report bugs to the Bioperl bug tracking system to help us keep track
71             of the bugs and their resolution. Bug reports can be submitted via the
72             web:
73              
74             https://github.com/bioperl/bioperl-live/issues
75              
76             =head1 AUTHOR - Steve Chervitz
77              
78             Email sac@bioperl.org
79              
80             =head1 APPENDIX
81              
82             The rest of the documentation details each of the object methods.
83             Internal methods are usually preceded with a _
84              
85             =cut
86              
87              
88             # Let the code begin...
89              
90              
91             package Bio::Search::Result::BlastResult;
92 12     12   80 use strict;
  12         27  
  12         375  
93              
94 12     12   3856 use Bio::Search::BlastStatistics;
  12         27  
  12         287  
95              
96 12     12   62 use base qw(Bio::Search::Result::GenericResult);
  12         17  
  12         13908  
97              
98             =head2 new
99              
100             Title : new
101             Usage : my $obj = Bio::Search::Result::BlastResult->new();
102             Function: Builds a new Bio::Search::Result::BlastResult object
103             Returns : Bio::Search::Result::BlastResult
104             Args : See Bio::Search::Result::GenericResult();
105             The following parameters are specific to BlastResult:
106             -iterations => array ref of Bio::Search::Iteration::IterationI objects
107             -inclusion_threshold => e-value threshold for inclusion in the
108             PSI-BLAST score matrix model (blastpgp)
109              
110             =cut
111              
112             sub new {
113 96     96 1 656 my($class,@args) = @_;
114              
115 96         598 my $self = $class->SUPER::new(@args);
116              
117 96         299 $self->{'_iterations'} = [];
118 96         231 $self->{'_iteration_index'} = 0;
119 96         178 $self->{'_iteration_count'} = 0;
120              
121 96         475 my( $iters, $ithresh ) = $self->_rearrange([qw(ITERATIONS
122             INCLUSION_THRESHOLD)],@args);
123              
124 96         281 $self->{'_inclusion_threshold'} = $ithresh; # This is a read-only variable
125              
126 96 50       245 if( defined $iters ) {
127 96 50       411 $self->throw("Must define arrayref of Iterations when initializing a $class\n") unless ref($iters) =~ /array/i;
128              
129 96         150 foreach my $i ( @{$iters} ) {
  96         206  
130 93         337 $self->add_iteration($i);
131             }
132             }
133             else {
134             # This shouldn't get called with the new SearchIO::blast.
135 0         0 print STDERR "BlastResult::new(): Not adding iterations.\n";
136 0         0 $self->{'_no_iterations'} = 1;
137             }
138              
139 96         477 return $self;
140             }
141              
142              
143             =head2 hits
144              
145             Title : hits
146             Usage : my @hits = $result->hits
147             Function: Returns the available hits for this Result
148             Returns : Array of L objects
149             Args : none
150             Note : This method overrides L to
151             take into account the possibility of multiple iterations, as occurs
152             in PSI-BLAST reports.
153             If there are multiple iterations, all 'new' hits for all iterations
154             are returned. These are the hits that did not occur in a previous
155             iteration.
156              
157             See Also: L
158              
159             =cut
160              
161             sub hits {
162 9     9 1 25 my ($self) = shift;
163 9 50       28 if ($self->{'_no_iterations'}) {
164 0         0 return $self->SUPER::hits;
165             }
166 9         24 my @hits = ();
167 9         26 foreach my $it ($self->iterations) {
168 9         37 push @hits, $it->hits;
169             }
170 9         45 return @hits;
171             }
172              
173             =head2 next_hit
174              
175             Title : next_hit
176             Usage : while( $hit = $result->next_hit()) { ... }
177             Function: Returns the next available Hit object, representing potential
178             matches between the query and various entities from the database.
179             Returns : a Bio::Search::Hit::HitI object or undef if there are no more.
180             Args : none
181             Note : This method overrides L
182             to take into account the possibility of multiple iterations, as
183             occurs in PSI-BLAST reports.
184              
185             If there are multiple iterations, calling next_hit() traverses the
186             all of the hits, old and new, for each iteration, calling next_hit()
187             on each iteration.
188              
189             See Also: L
190              
191             =cut
192              
193             sub next_hit {
194 1381     1381 1 78816 my ($self,@args) = @_;
195 1381 50       2731 if ($self->{'_no_iterations'}) {
196 0         0 return $self->SUPER::next_hit(@args);
197             }
198              
199 1381         1500 my $iter_index;
200 1381 100       2483 if (not defined $self->{'_last_hit'}) {
201 111         344 $iter_index = $self->{'_iter_index'} = $self->_next_iteration_index;
202             } else {
203 1270         1585 $iter_index = $self->{'_iter_index'};
204             }
205              
206 1381 100       1640 return if $iter_index >= scalar @{$self->{'_iterations'}};
  1381         2621  
207              
208 1349         1842 my $it = $self->{'_iterations'}->[$iter_index];
209 1349         2920 my $hit = $self->{'_last_hit'} = $it->next_hit;
210              
211 1349 100       3234 return defined($hit) ? $hit : $self->next_hit;
212             }
213              
214              
215             =head2 num_hits
216              
217             Title : num_hits
218             Usage : my $hitcount= $result->num_hits
219             Function: returns the number of hits for this query result
220             Returns : integer
221             Args : none
222             Note : This method overrides L
223             to take into account the possibility of multiple iterations, as
224             occurs in PSI-BLAST reports.
225              
226             If there are multiple iterations, calling num_hits() returns the
227             number of 'new' hits for each iteration. These are the hits that did
228             not occur in a previous iteration.
229              
230             See Also: L
231              
232             =cut
233              
234             sub num_hits{
235 3     3 1 10 my ($self) = shift;
236 3 50       12 if ($self->{'_no_iterations'}) {
237 0         0 return $self->SUPER::num_hits;
238             }
239 3 50       9 if (not defined $self->{'_iterations'}) {
240 0         0 $self->throw("Can't get Hits: data not collected.");
241             }
242 3         9 return scalar( $self->hits );
243             }
244              
245             =head2 add_hit
246              
247             Title : add_hit
248             Usage : $report->add_hit($hit)
249             Function: Adds a HitI to the stored list of hits
250             Returns : Number of HitI currently stored
251             Args : Bio::Search::Hit::HitI
252              
253             =cut
254              
255             sub add_hit {
256 1     1 1 12 my ($self,$hit) = @_;
257 1         6 my $iter = $self->iteration;
258 1 50       9 if( $hit->isa('Bio::Search::Hit::HitI') ) {
259 1         8 return $iter->add_hit(-hit => $hit);
260             } else {
261 0         0 $self->throw("Passed in a " .ref($hit).
262             " as a Iteration which is not a Bio::Search::Hit::HitI.");
263             }
264 0         0 return $iter->num_hits;
265             }
266              
267             =head2 add_iteration
268              
269             Title : add_iteration
270             Usage : $report->add_iteration($iteration)
271             Function: Adds a IterationI to the stored list of iterations
272             Returns : Number of IterationI currently stored
273             Args : Bio::Search::Iteration::IterationI
274              
275             =cut
276              
277             sub add_iteration {
278 93     93 1 227 my ($self,$i) = @_;
279 93 50       448 if( $i->isa('Bio::Search::Iteration::IterationI') ) {
280 93         154 push @{$self->{'_iterations'}}, $i;
  93         258  
281 93         181 $self->{'_iteration_count'}++;
282             } else {
283 0         0 $self->throw("Passed in a " .ref($i).
284             " as a Iteration which is not a Bio::Search::Iteration::IterationI.");
285             }
286 93         165 return scalar @{$self->{'_iterations'}};
  93         216  
287             }
288              
289              
290             =head2 next_iteration
291              
292             Title : next_iteration
293             Usage : while( $it = $result->next_iteration()) { ... }
294             Function: Returns the next Iteration object, representing all hits
295             found within a given PSI-Blast iteration.
296             Returns : a Bio::Search::Iteration::IterationI object or undef if there are no more.
297             Args : none
298              
299             =cut
300              
301             sub next_iteration {
302 3     3 1 8 my ($self) = @_;
303              
304 3 100       15 unless($self->{'_iter_queue_started'}) {
305 1         4 $self->{'_iter_queue'} = [$self->iterations()];
306 1         3 $self->{'_iter_queue_started'} = 1;
307             }
308 3         6 return shift @{$self->{'_iter_queue'}};
  3         10  
309             }
310              
311             =head2 iteration
312              
313             Usage : $iteration = $blast->iteration( $number );
314             Purpose : Get an IterationI object for the specified iteration
315             in the search result (PSI-BLAST).
316             Returns : Bio::Search::Iteration::IterationI object
317             Throws : Bio::Root::NoSuchThing exception if $number is not within
318             range of the number of iterations in this report.
319             Argument : integer (optional, if not specified get the last iteration)
320             First iteration = 1
321              
322             =cut
323              
324             sub iteration {
325 1     1 1 3 my ($self,$num) = @_;
326 1 50       5 $num = scalar @{$self->{'_iterations'}} unless defined $num;
  1         4  
327 1 50 33     11 unless ($num >= 1 and $num <= scalar $self->{'_iteration_count'}) {
328 0         0 $self->throw(-class=>'Bio::Root::NoSuchThing',
329             -text=>"No such iteration number: $num. Valid range=1-$self->{'_iteration_count'}",
330             -value=>$num);
331             }
332 1         5 return $self->{'_iterations'}->[$num-1];
333             }
334              
335             =head2 num_iterations
336              
337             Usage : $num_iterations = $blast->num_iterations;
338             Purpose : Get the number of iterations in the search result (PSI-BLAST).
339             Returns : Total number of iterations in the report
340             Argument : none (read-only)
341              
342             =cut
343              
344 0     0 1 0 sub num_iterations { shift->{'_iteration_count'} }
345              
346             # Methods provided for consistency with BPpsilite.pm (now deprecated);
347             # these are now merely synonyms
348              
349             =head2 number_of_iterations
350              
351             Usage : $num_iterations = $blast->number_of_iterations;
352             Purpose : Get the number of iterations in the search result (PSI-BLAST).
353             Returns : Total number of iterations in the report
354             Argument : none (read-only)
355             Note : Alias of L.
356              
357             =cut
358              
359 0     0 1 0 sub number_of_iterations { shift->num_iterations }
360              
361             =head2 round
362              
363             Usage : $round = $blast->round( $number );
364             Purpose : Get an IterationI object for the specified iteration
365             in the search result (PSI-BLAST).
366             Returns : Bio::Search::Iteration::IterationI object
367             Throws : Bio::Root::NoSuchThing exception if $number is not within
368             range of the number of iterations in this report.
369             Argument : integer (optional, if not specified get the last iteration)
370             First iteration = 1
371             Note : Alias of L.
372              
373             =cut
374              
375 0     0 1 0 sub round { shift->iteration(@_) }
376              
377              
378             =head2 iterations
379              
380             Title : iterations
381             Usage : my @iterations = $result->iterations
382             Function: Returns the IterationI objects contained within this Result
383             Returns : Array of L objects
384             Args : none
385              
386             =cut
387              
388             sub iterations {
389 15     15 1 28 my $self = shift;
390 15         32 my @its = ();
391 15 50       115 if( ref($self->{'_iterations'}) =~ /ARRAY/i ) {
392 15         150 @its = @{$self->{'_iterations'}};
  15         47  
393             }
394 15         40 return @its;
395             }
396              
397             =head2 psiblast
398              
399             Usage : if( $blast->psiblast ) { ... }
400             Purpose : Set/get a boolean indicator whether or not the report
401             is a PSI-BLAST report.
402             Returns : 1 if PSI-BLAST, undef if not.
403             Argument : 1 (when setting)
404              
405             =cut
406              
407             #----------------
408             sub psiblast {
409             #----------------
410 0     0 1 0 my ($self, $val ) = @_;
411 0 0       0 if( $val ) {
412 0         0 $self->{'_psiblast'} = 1;
413             }
414 0         0 return $self->{'_psiblast'};
415             }
416              
417              
418             =head2 no_hits_found
419              
420             Usage : $nohits = $blast->no_hits_found( $iteration_number );
421             Purpose : Get boolean indicator indicating whether or not any hits
422             were present in the report.
423              
424             This is NOT the same as determining the number of hits via
425             the hits() method, which will return zero hits if there were no
426             hits in the report or if all hits were filtered out during the parse.
427              
428             Thus, this method can be used to distinguish these possibilities
429             for hitless reports generated when filtering.
430              
431             Returns : Boolean
432             Argument : (optional) integer indicating the iteration number (PSI-BLAST)
433             If iteration number is not specified and this is a PSI-BLAST result,
434             then this method will return true only if all iterations had
435             no hits found.
436              
437             =cut
438              
439             sub no_hits_found {
440 0     0 1 0 my ($self, $round) = @_;
441              
442 0         0 my $result = 0; # final return value of this method.
443             # Watch the double negative!
444             # result = 0 means "yes hits were found"
445             # result = 1 means "no hits were found" (for the indicated iteration or all iterations)
446              
447             # If a iteration was not specified and there were multiple iterations,
448             # this method should return true only if all iterations had no hits found.
449 0 0       0 if( not defined $round ) {
450 0 0       0 if( $self->{'_iterations'} > 1) {
451 0         0 $result = 1;
452 0         0 foreach my $i( 1..$self->{'_iterations'} ) {
453 0 0       0 if( not defined $self->{"_iteration_$i"}->{'_no_hits_found'} ) {
454 0         0 $result = 0;
455 0         0 last;
456             }
457             }
458             }
459             else {
460 0         0 $result = $self->{"_iteration_1"}->{'_no_hits_found'};
461             }
462             }
463             else {
464 0         0 $result = $self->{"_iteration_$round"}->{'_no_hits_found'};
465             }
466              
467 0         0 return $result;
468             }
469              
470              
471             =head2 set_no_hits_found
472              
473             Usage : $blast->set_no_hits_found( $iteration_number );
474             Purpose : Set boolean indicator indicating whether or not any hits
475             were present in the report.
476             Returns : n/a
477             Argument : (optional) integer indicating the iteration number (PSI-BLAST)
478              
479             =cut
480              
481             sub set_no_hits_found {
482 0     0 1 0 my ($self, $round) = @_;
483 0   0     0 $round ||= 1;
484 0         0 $self->{"_iteration_$round"}->{'_no_hits_found'} = 1;
485             }
486              
487             =head2 _next_iteration_index
488              
489             Title : _next_iteration_index
490             Usage : private
491              
492             =cut
493              
494             sub _next_iteration_index{
495 111     111   195 my ($self,@args) = @_;
496 111         379 return $self->{'_iteration_index'}++;
497             }
498              
499              
500             =head2 rewind
501              
502             Title : rewind
503             Usage : $result->rewind;
504             Function: Allow one to reset the Iteration iterator to the beginning
505             Since this is an in-memory implementation
506             Returns : none
507             Args : none
508              
509             =cut
510              
511             sub rewind {
512 5     5 1 10 my $self = shift;
513 5         44 $self->SUPER::rewind(@_);
514 5         12 $self->{'_iteration_index'} = 0;
515 5         20 foreach ($self->iterations) {
516 5         29 $_->rewind;
517             }
518             }
519              
520              
521             =head2 inclusion_threshold
522              
523             Title : inclusion_threshold
524             Usage : my $incl_thresh = $result->inclusion_threshold; (read-only)
525             Function: Gets the e-value threshold for inclusion in the PSI-BLAST
526             score matrix model (blastpgp) that was used for generating the report
527             being parsed.
528             Returns : number (real) or undef if not a PSI-BLAST report.
529             Args : none
530              
531             =cut
532              
533             sub inclusion_threshold {
534 0     0 1   my $self = shift;
535 0           return $self->{'_inclusion_threshold'};
536             }
537              
538             1;