File Coverage

blib/lib/Bio/NEXUS/TaxUnitSet.pm
Criterion Covered Total %
statement 145 197 73.6
branch 14 32 43.7
condition 5 6 83.3
subroutine 28 32 87.5
pod 24 24 100.0
total 216 291 74.2


line stmt bran cond sub pod time code
1             #################################################################
2             # TaxUnitSet.pm
3             #################################################################
4             # Author: Chengzhi Liang, Peter Yang, Thomas Hladish
5             # $Id: TaxUnitSet.pm,v 1.30 2007/09/24 04:52:14 rvos Exp $
6              
7             #################### START POD DOCUMENTATION ##################
8              
9             =head1 NAME
10              
11             Bio::NEXUS::TaxUnitSet - Represents a sets of OTUS (Bio::NEXUS::TaxUnits objects) in a NEXUS file
12              
13             =head1 SYNOPSIS
14              
15             $otuset = new Bio::NEXUS::TaxUnitSet(\@otus);
16              
17             =head1 DESCRIPTION
18              
19             This module represents a set of OTUs (Bio::NEXUS::TaxUnit objects) in a NEXUS file (in characters block or History block)
20              
21             =head1 COMMENTS
22              
23             =head1 FEEDBACK
24              
25             All feedback (bugs, feature enhancements, etc.) are greatly appreciated.
26              
27             =head1 AUTHORS
28              
29             Chengzhi Liang (liangc@umbi.umd.edu)
30             Peter Yang (pyang@rice.edu)
31             Thomas Hladish (tjhladish at yahoo)
32              
33             =head1 VERSION
34              
35             $Revision: 1.30 $
36              
37             =head1 METHODS
38              
39             =cut
40              
41             package Bio::NEXUS::TaxUnitSet;
42              
43 34     34   189 use strict;
  34         69  
  34         1118  
44 34     34   223 use Bio::NEXUS::Functions;
  34         77  
  34         9449  
45 34     34   23916 use Bio::NEXUS::TaxUnit;
  34         91  
  34         1133  
46             #use Data::Dumper; # XXX this is not used, might as well not import it!
47             #use Carp; # XXX this is not used, might as well not import it!
48 34     34   216 use Bio::NEXUS::Util::Exceptions 'throw';
  34         68  
  34         1720  
49 34     34   2747 use Bio::NEXUS::Util::Logger;
  34         64  
  34         2285  
50 34     34   181 use vars qw($VERSION $AUTOLOAD);
  34         72  
  34         1821  
51 34     34   174 use Bio::NEXUS; $VERSION = $Bio::NEXUS::VERSION;
  34         63  
  34         105150  
52              
53             my $logger = Bio::NEXUS::Util::Logger->new;
54              
55             =head2 new
56              
57             Title : new
58             Usage : $otuset = new Bio::NEXUS::TaxUnitSet(\@otus);
59             Function: Creates a new Bio::NEXUS::TaxUnitSet object
60             Returns : Bio::NEXUS::TaxUnitSet object
61             Args : ref to an array of TaxUnit objects
62              
63             =cut
64              
65             sub new {
66 78     78 1 191 my ( $class, $otus ) = @_;
67 78         328 my $self = { otus => $otus, };
68 78         238 bless( $self, $class );
69 78         380 return $self;
70             }
71              
72             =head2 clone
73              
74             Title : clone
75             Usage : my $newset = $set->clone();
76             Function: clone an TaxUnitSet object
77             Returns : TaxUnitSet object
78             Args : none
79              
80             =cut
81              
82             sub clone {
83 1     1 1 6 my ($self) = @_;
84 1         8 my $class = ref($self);
85 1         2 my $newset = bless( { %{$self} }, $class );
  1         5  
86 1         3 my @otus = @{ $newset->get_otus() };
  1         5  
87 1         3 my @newotus = ();
88 1         3 for my $otu (@otus) {
89 2         8 push @newotus, $otu->clone();
90             }
91 1         5 $newset->set_otus( \@newotus );
92 1         4 return $newset;
93             }
94              
95             =head2 add_otu
96              
97             Title : add_otu
98             Usage : $block->add_otu($otu);
99             Function: add a taxon
100             Returns : none
101             Args : a taxon
102              
103             =cut
104              
105             sub add_otu {
106 12     12 1 24 my ( $self, $otu ) = @_;
107 12         24 push @{ $self->{'otus'} }, $otu;
  12         68  
108             }
109              
110             =head2 set_otus
111              
112             Title : set_otus
113             Usage : $set->set_otus($otus);
114             Function: sets the list of OTUs
115             Returns : none
116             Args : array of OTUs
117              
118             =cut
119              
120             sub set_otus {
121 74     74 1 170 my ( $self, $otus ) = @_;
122 74         345 $self->{'otus'} = $otus;
123             }
124              
125             =head2 get_otus
126              
127             Title : get_otus
128             Usage : $set->get_otus();
129             Function: Returns array of otus
130             Returns : all otus
131             Args : none
132              
133             =cut
134              
135             sub get_otus {
136 144     144 1 7969 my ($self) = @_;
137 144         524 return $self->{'otus'};
138             }
139              
140             =head2 get_otu
141              
142             Title : get_otu
143             Usage : $set->get_otu(name);
144             Function: Returns an OTU with a specified name
145             Returns : an OTU (Bio::NEXUS::TaxUnit)
146             Args : OTU name as scalar string
147              
148             =cut
149              
150             sub get_otu {
151 13     13 1 27 my ( $self, $name ) = @_;
152 13         21 for my $otu ( @{ $self->get_otus() } ) {
  13         35  
153 50 100       154 return $otu if ( lc($name) eq lc($otu->get_name()) );
154             }
155 0         0 return undef;
156             }
157              
158             =head2 get_otu_names
159              
160             Title : get_otu_names
161             Usage : $set->get_otu_names();
162             Function: Returns array of OTU names
163             Returns : all OTU names
164             Args : none
165              
166             =cut
167              
168             sub get_otu_names {
169 77     77 1 178 my ($self) = @_;
170 77         221 my @names = ();
171 77         148 for my $otu ( @{ $self->get_otus() } ) {
  77         320  
172 532         1454 push @names, $otu->get_name();
173             }
174              
175             # @names = sort {lc $a cmp lc $b} @names;
176 77         473 return \@names;
177             }
178              
179             =head2 get_seq_string_hash
180              
181             Title : get_seq_string_hash
182             Usage : $set->get_seq_string_hash($delimiter);
183             Function: gets sequence string delimited by $delimiter (default is "")
184             Returns : hashref
185             Args : scalar
186              
187             =cut
188              
189             sub get_seq_string_hash {
190 0     0 1 0 my ( $self, $delimiter ) = @_;
191 0         0 my %sequences;
192 0 0       0 $delimiter = '' unless $delimiter;
193 0         0 for my $otu ( @{ $self->get_otus() } ) {
  0         0  
194 0         0 $sequences{ $otu->get_name() } = join $delimiter, @{ $otu->get_seq() };
  0         0  
195             }
196 0         0 return \%sequences;
197             }
198              
199             =head2 get_seq_array_hash
200              
201             Title : get_seq_array_hash
202             Usage : $set->get_seq_array_hash();
203             Function: gets sequences as arrays
204             Returns : hashref
205             Args : scalar
206              
207             =cut
208              
209             sub get_seq_array_hash {
210 8     8 1 24 my ($self) = @_;
211 8         17 my %sequences;
212 8         17 for my $otu ( @{ $self->get_otus() } ) {
  8         31  
213 47         130 $sequences{ $otu->get_name() } = $otu->get_seq();
214             }
215 8         35 return \%sequences;
216             }
217              
218             =head2 rename_otus
219              
220             Title : rename_otus
221             Usage : $set->rename_otus($names);
222             Function: rename all OTUs
223             Returns : none
224             Args : hash of OTU names
225              
226             =cut
227              
228             sub rename_otus {
229 4     4 1 9 my ( $self, $translate ) = @_;
230 4         9 for my $otu ( @{ $self->get_otus() } ) {
  4         16  
231 14         69 my $name = $otu->get_name();
232 14         27 my $newname = $translate->{$name};
233 14 100       54 if ($newname) {
234 4         19 $otu->set_name($newname);
235             }
236             }
237             }
238              
239             =head2 subset
240              
241             Title : subset
242             Usage : $block->subset($otunames);
243             Function: select a subset of OTUs
244             Returns : new TaxUnitSet object
245             Args : OTU names
246              
247             =cut
248              
249             sub subset {
250 0     0 1 0 my ( $self, $otunames ) = @_;
251 0         0 my $names = " @{$otunames} ";
  0         0  
252 0         0 my @newarray;
253 0         0 for my $otu ( @{ $self->get_otus() } ) {
  0         0  
254 0         0 my $name = $otu->get_name();
255 0 0       0 if ( $names =~ /\s+$name\s+/ ) {
256 0         0 push @newarray, $otu;
257             }
258             }
259 0         0 my $newset = new Bio::NEXUS::TaxUnitSet( \@newarray );
260 0         0 $newset->set_charlabels( $self->get_charlabels );
261 0         0 $newset->set_charstatelabels( $self->get_charstatelabels );
262 0         0 return $newset;
263             }
264              
265             =head2 select_columns
266              
267             Title : select_columns
268             Usage : $set->select_columns($columns);
269             Function: select a subset of characters
270             Returns : new $self with subset of columns of characters
271             Args : column numbers
272              
273             =cut
274              
275             sub select_columns {
276 1     1 1 2 my ( $self, $columns ) = @_;
277 1         5 $self->select_charlabels($columns);
278 1         5 $self->select_charstatelabels($columns);
279 1         4 $self->select_chars($columns);
280 1         2 return $self;
281             }
282              
283             =head2 select_chars
284              
285             Title : select_chars
286             Usage : $set->select_chars($columns);
287             Function: select a subset of characters
288             Returns : new self with subset of characters
289             Args : column numbers
290              
291             =cut
292              
293             sub select_chars {
294 1     1 1 2 my ( $self, $columns ) = @_;
295 1         2 my @otus = @{ $self->get_otus() };
  1         4  
296 1         3 for my $otu (@otus) {
297 1         2 my @seq = @{ $otu->get_seq() };
  1         6  
298 1         2 my @newseq;
299 1         2 for my $i ( @{$columns} ) {
  1         3  
300 1 50       4 if ( $i >= scalar @seq ) {
301 0         0 throw 'BadArgs' => "invalid column number: " . ( $i + 1 );
302             }
303 1         4 push @newseq, $seq[$i];
304             }
305 1         7 $otu->set_seq( \@newseq );
306             }
307 1         2 return $self;
308             }
309              
310             =head2 set_charlabels
311              
312             Title : set_charlabels
313             Usage : $set->set_charlabels($labels);
314             Function: Set the character names
315             Returns : none
316             Args : array of character names
317              
318             =cut
319              
320             sub set_charlabels {
321 35     35 1 69 my ( $self, $labels ) = @_;
322 35         63 my $charstates;
323 35         152 for ( my $i = 0; $i < @$labels; $i++ ) {
324 220         1214 push @$charstates,
325             { id => $i + 1, charlabel => $$labels[$i], states => {} }
326              
327             }
328 35         188 $self->{'charstates'} = $charstates;
329             }
330              
331             =head2 get_charlabels
332              
333             Title : get_charlabels
334             Usage : $set->get_charlabels();
335             Function: Returns an array of character labels
336             Returns : character names
337             Args : none
338              
339             =cut
340              
341             sub get_charlabels {
342 6     6 1 16 my ($self) = @_;
343 6         12 my $charlabels;
344 6         14 for my $charstate ( @{ $self->{'charstates'} } ) {
  6         26  
345 15         43 push @$charlabels, $charstate->{'charlabel'};
346             }
347 6   100     71 return $charlabels || [];
348             }
349              
350             =head2 set_statelabels
351              
352             Title : set_statelabels
353             Usage : $set->set_statelabels($labels);
354             Function: Set the state names
355             Returns : none
356             Args : array of state names
357              
358             =cut
359              
360             sub set_statelabels {
361 0     0 1 0 my ( $self, $labels ) = @_;
362 0         0 $self->{'statelabels'} = $labels;
363             }
364              
365             =head2 get_statelabels
366              
367             Title : get_statelabels
368             Usage : $set->get_statelabels();
369             Function: Returns an array of state labels
370             Returns : state names
371             Args : none
372              
373             =cut
374              
375             sub get_statelabels {
376 144     144 1 260 my ($self) = @_;
377 144   50     1268 return $self->{'statelabels'} || [];
378             }
379              
380             =head2 set_charstatelabels
381              
382             Title : set_charstatelabels
383             Usage : $set->set_charstatelabels($labels);
384             Function: Set the character names and states
385             Returns : none
386             Args : array of character states
387              
388             =cut
389              
390             sub set_charstatelabels {
391 73     73 1 152 my ( $self, $states ) = @_;
392 73         261 $self->{'charstatelabels'} = $states;
393             }
394              
395             =head2 get_charstatelabels
396              
397             Title : get_charstatelabels
398             Usage : $set->get_charstatelabels();
399             Function: Returns an array of character states
400             Returns : character states
401             Args : none
402              
403             =cut
404              
405             sub get_charstatelabels {
406 77     77 1 158 my ($self) = @_;
407 77   100     622 return $self->{'charstatelabels'} || [];
408             }
409              
410             =head2 get_ntax
411              
412             Title : get_ntax
413             Usage : $set->get_ntax();
414             Function: Returns the number of taxa of the block
415             Returns : # taxa
416             Args : none
417              
418             =cut
419              
420             sub get_ntax {
421 5     5 1 10 my $self = shift;
422 5         21 my $otus = $self->get_otus();
423 5 50       20 if ( ref $otus ) {
424 5         8 return scalar @{ $self->get_otus() };
  5         13  
425             }
426             else {
427 0         0 $logger->warn("No otus found\n")
428             }
429             }
430              
431             =head2 get_nchar
432              
433             Title : get_nchar
434             Usage : $set->get_nchar();
435             Function: Returns the number of characters of the block
436             Returns : # charaters
437             Args : none
438              
439             =cut
440              
441             sub get_nchar {
442 2     2 1 4 my $self = shift;
443 2         3 return scalar @{ $self->get_otus()->[0]->get_seq() };
  2         6  
444             }
445              
446             =head2 select_charlabels
447              
448             Title : select_charlabels
449             Usage : $set->select_charlabels($columns);
450             Function: select a subset of charlabels
451             Returns : new self with subset of charlabels
452             Args : column numbers
453              
454             =cut
455              
456             sub select_charlabels {
457 1     1 1 2 my ( $self, $columns ) = @_;
458 1         2 my @labels = @{ $self->get_charlabels() };
  1         6  
459 1 50       4 if ( @labels == 0 ) { return; }
  1         2  
460              
461 0         0 my @newlabels = ();
462 0         0 for my $i ( @{$columns} ) {
  0         0  
463 0         0 push @newlabels, $labels[$i];
464             }
465              
466 0         0 $self->set_charlabels( \@newlabels );
467 0         0 return $self;
468             }
469              
470             =head2 select_charstatelabels
471              
472             Title : select_charstatelabels
473             Usage : $set->select_charstatelabels($columns);
474             Function: select a subset of charstates
475             Returns : new self with subset of charstates
476             Args : column numbers
477              
478             =cut
479              
480             sub select_charstatelabels {
481 1     1 1 2 my ( $self, $columns ) = @_;
482 1         1 my @labels = @{ $self->get_charstatelabels() };
  1         3  
483 1 50       4 if ( @labels == 0 ) { return; }
  0         0  
484              
485 1         3 my @newlabels = ();
486 1         2 for my $i ( @{$columns} ) {
  1         3  
487 1         4 push @newlabels, $labels[$i];
488             }
489              
490 1         4 $self->set_charstatelabels( \@newlabels );
491 1         2 return $self;
492             }
493              
494             =head2 equals
495              
496             Name : equals
497             Usage : $set->equals($another);
498             Function: compare if two TaxUnitSet objects are equal
499             Returns : boolean
500             Args : an TaxUnitSet object
501              
502             =cut
503              
504             sub equals {
505 9     9 1 17 my ( $self, $set ) = @_;
506 9         12 my @otus1 = @{ $self->get_otus() };
  9         25  
507 9         14 my @otus2 = @{ $set->get_otus() };
  9         19  
508 9 50       25 if ( @otus1 != @otus2 ) { return 0; }
  0         0  
509 9         33 @otus1 = sort { $a->get_name() cmp $b->get_name() } @otus1;
  67         172  
510 9         22 @otus2 = sort { $a->get_name() cmp $b->get_name() } @otus2;
  64         154  
511 9         40 for ( my $i = 0; $i < @otus1; $i++ ) {
512              
513             # check names
514 35 50       100 if ( $otus1[$i]->get_name() ne $otus2[$i]->get_name() ) {
515             #carp "OTU names not equal: " . $otus1[$i]->get_name() . " ne " . $otus2[$i]->get_name() . "\n";
516 0         0 return 0;
517             }
518              
519             # check seq's
520 35         46 my @seqs1 = @{ $otus1[$i]->get_seq() };
  35         94  
521 35         46 my @seqs2 = @{ $otus2[$i]->get_seq() };
  35         91  
522              
523 35 50       94 if ( @seqs1 != @seqs2 ) { return 0; }
  0         0  
524 35         88 for ( my $j = 0; $j < @seqs1; $j++ ) {
525              
526             # entry is an array ref of probability values
527 1160 50       3836 if ( ref( $seqs1[$j] ) eq 'ARRAY' ) {
    100          
528 0         0 my @prob1 = @{ $seqs1[$j] };
  0         0  
529 0         0 my @prob2 = @{ $seqs2[$j] };
  0         0  
530 0         0 for ( my $k = 0; $k < @prob1; $k++ ) {
531 0 0       0 if ( $prob1[$k] != $prob2[$k] ) {
532 0         0 return 0;
533             }
534             }
535             }
536              
537             # entry is a character datum
538             elsif ( $seqs1[$j] ne $seqs2[$j] ) {
539             #carp "Character values not equal: $seqs1[$j] != $seqs2[$j]\n";
540 3         158 return 0;
541             }
542             }
543              
544             }
545 6         305 return 1;
546             }
547              
548             sub AUTOLOAD {
549 0 0   0     return if $AUTOLOAD =~ /DESTROY$/;
550 0           my $package_name = __PACKAGE__ . '::';
551              
552             # The following methods are deprecated and are temporarily supported
553             # via a warning and a redirection
554 0           my %synonym_for = (
555             "${package_name}set_charstates" => "${package_name}set_charstatelabels",
556             "${package_name}get_charstates" => "${package_name}get_charstatelabels",
557             "${package_name}select_charstates" =>
558             "${package_name}select_charstatelabels",
559             "${package_name}get_otu_sequences" =>
560             "${package_name}get_seq_string_hash",
561             );
562              
563 0 0         if ( defined $synonym_for{$AUTOLOAD} ) {
564 0           $logger->warn("$AUTOLOAD() is deprecated; use $synonym_for{$AUTOLOAD}() instead");
565 0           goto &{ $synonym_for{$AUTOLOAD} };
  0            
566             }
567             else {
568 0           throw 'UnknownMethod' => "ERROR: Unknown method $AUTOLOAD called";
569             }
570 0           return;
571             }
572              
573             1;