File Coverage

blib/lib/Bio/Polloc/LociGroup.pm
Criterion Covered Total %
statement 44 140 31.4
branch 13 74 17.5
condition 3 44 6.8
subroutine 11 16 68.7
pod 12 12 100.0
total 83 286 29.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Bio::Polloc::LociGroup - A group of loci
4              
5             =head1 AUTHOR - Luis M. Rodriguez-R
6              
7             Email lmrodriguezr at gmail dot com
8              
9             =head1 IMPLEMENTS OR EXTENDS
10              
11             =over
12              
13             =item *
14              
15             L<Bio::Polloc::Polloc::Root>
16              
17             =back
18              
19             =cut
20              
21             package Bio::Polloc::LociGroup;
22 5     5   27 use strict;
  5         10  
  5         161  
23 5     5   23 use base qw(Bio::Polloc::Polloc::Root);
  5         10  
  5         332  
24 5     5   26 use Bio::Polloc::Polloc::IO;
  5         9  
  5         8861  
25             our $VERSION = 1.0503; # [a-version] from Bio::Polloc::Polloc::Version
26              
27              
28             =head1 PUBLIC METHODS
29              
30             Methods provided by the package
31              
32             =cut
33              
34             =head2 new
35              
36             The basic initialization method
37              
38             =cut
39              
40             sub new {
41 3     3 1 7 my($caller,@args) = @_;
42 3         20 my $self = $caller->SUPER::new(@args);
43 3         11 $self->_initialize(@args);
44 3         11 return $self;
45             }
46              
47             =head2 add_locus
48              
49             Alias of C<add_loci()>
50              
51             =cut
52              
53 87     87 1 188 sub add_locus { return shift->add_loci(@_) }
54              
55             =head2 add_loci
56              
57             Adds loci to the collection on the specified
58             genome's space
59              
60             B<Throws>
61              
62             A L<Bio::Polloc::Polloc::Error> if an argument is not
63             a L<Bio::Polloc::LocusI> object.
64              
65             B<Arguments>
66              
67             The first argument B<can> be the identifier of
68             the genome's space (int). All the following are
69             expected to be L<Bio::Polloc::LocusI> objects.
70              
71             =cut
72              
73             sub add_loci {
74 87     87 1 130 my ($self,@l) = @_;
75 87         81 my $space;
76 87 50 33     420 if(defined $l[0] and not ref $l[0]){
77 0         0 $space = 0 + shift @l;
78             }
79 87 100       194 $self->{'_loci'} = [] unless defined $self->{'_loci'};
80 87         126 for my $locus (@l){
81 87         175 $self->debug("Saving locus (".($#l+1)." loci, cur:".($#{$self->{'_loci'}}+1).")");
  87         343  
82 87 50 33     631 $self->throw('Expecting a Bio::Polloc::LocusI object', $locus)
83             unless UNIVERSAL::can($locus, 'isa') and $locus->isa('Bio::Polloc::LocusI');
84 87 50       163 $locus->genome($self->genomes->[$space]) if defined $space;
85 87         97 push @{ $self->{'_loci'} }, $locus;
  87         509  
86             }
87             }
88              
89             =head2 loci
90              
91             Gets the loci
92              
93             =cut
94              
95             sub loci {
96 9     9 1 2437 my $self = shift;
97 9 50       31 $self->{'_loci'} = [] unless defined $self->{'_loci'};
98 9         52 return $self->{'_loci'};
99             }
100              
101             =head2 structured_loci
102              
103             Returns a two-dimensional array where the first key corresponds
104             to the number of the genome space and the second key is an
105             incremental for each locus.
106              
107             B<Note>
108              
109             This function is provided for convenience in some output formating,
110             but its use should be avoided as it causes a huge processing time
111             penalty.
112              
113             B<Warning>
114              
115             Loci without defined genome will not be included in the output.
116              
117             =cut
118              
119             sub structured_loci {
120 0     0 1 0 my $self = shift;
121 0 0       0 return unless defined $self->genomes;
122 0         0 my $struct = [];
123 0         0 for my $locus (@{$self->loci}){
  0         0  
124 0 0       0 next unless defined $locus->genome;
125 0         0 my $space = 0;
126 0         0 for my $genome (@{$self->genomes}){
  0         0  
127 0 0       0 $struct->[$space] = [] unless defined $struct->[$space];
128 0 0       0 if($genome->name eq $locus->genome->name){
129 0         0 push @{ $struct->[$space] }, $locus;
  0         0  
130             }
131 0         0 $space++;
132             }
133             }
134 0         0 return $struct;
135             }
136              
137             =head2 locus
138              
139             Get a locus by ID
140              
141             B<Arguments>
142              
143             The ID of the locus (str).
144              
145             =cut
146              
147             sub locus {
148 0     0 1 0 my ($self, $id) = @_;
149 0 0       0 for my $locus (@{$self->loci}){ return $locus if $locus->id eq $id }
  0         0  
  0         0  
150 0         0 return;
151             }
152              
153             =head2 name
154              
155             Gets/sets the name of the group. This is supposed
156             to be unique!
157              
158             B<Note>
159              
160             Future implementations could assume unique naming
161             for getting/setting/initializing groups of loci
162             by name.
163              
164             =cut
165              
166             sub name {
167 3     3 1 5 my ($self, $value) = @_;
168 3 50       12 $self->{'_name'} = $value if defined $value;
169 3         6 return $self->{'_name'};
170             }
171              
172             =head2 genomes
173              
174             Gets/sets the genomes to be used as analysis base.
175              
176             B<Arguments>
177              
178             A reference to an array of L<Bio::Polloc::Genome> objects.
179              
180             =cut
181              
182             sub genomes {
183 3     3 1 6 my($self, $value) = @_;
184 3 100       9 $self->{'_genomes'} = $value if defined $value;
185 3 100       13 return unless defined $self->{'_genomes'};
186 1 50 33     10 $self->throw("Unexpected type of genomes collection", $self->{'_genomes'})
187             unless ref($self->{'_genomes'}) and ref($self->{'_genomes'})=~m/ARRAY/i;
188 1         3 return $self->{'_genomes'};
189             }
190              
191              
192             =head2 featurename
193              
194             Gets/Sets the name of the feature common to all the
195             loci in the group.
196              
197             =cut
198              
199             sub featurename {
200 3     3 1 5 my ($self, $value) = @_;
201 3 50       9 $self->{'_featurename'} = $value if defined $value;
202 3         5 return $self->{'_featurename'};
203             }
204              
205             =head2 avg_length
206              
207             Gets the average length of the stored loci.
208              
209             B<Returns>
210              
211             The average length (float) or an array containing the
212             average length (float) and the standard deviation (float),
213             depending on the expected output.
214              
215             B<Syntax>
216              
217             my $len = $locigroup->length;
218              
219             Or,
220              
221             my($len, $sd) = $locigroup->length;
222              
223             =cut
224              
225             sub avg_length {
226 0     0 1 0 my $self = shift;
227 0         0 my $len_avg = 0;
228 0         0 my $len_sd = 0;
229 0 0       0 if($#{$self->loci} >= 1){
  0 0       0  
  0         0  
230             # AVG
231 0         0 $len_avg+= abs($_->from - $_->to) for @{$self->loci};
  0         0  
232 0         0 $len_avg = $len_avg/($#{$self->loci}+1);
  0         0  
233 0 0       0 return $len_avg unless wantarray;
234             # SD
235 0         0 $len_sd+= (abs($_->from - $_->to) - $len_avg)**2 for @{$self->loci};
  0         0  
236 0         0 $len_sd = sqrt($len_sd/$#{$self->loci}); # n-1, not n (unbiased SD)
  0         0  
237             }elsif($#{$self->loci}==0){
238 0         0 $len_avg = abs($self->loci->[0]->from - $self->loci->[0]->to);
239             }
240 0 0       0 return wantarray ? ($len_avg, $len_sd) : $len_avg;
241             }
242              
243             =head2 align_context
244              
245             B<Arguments>
246              
247             Arguments work in the same way L<Bio::Polloc::LocusI-E<gt>context_seq()>
248             arguments do.
249              
250             =over
251              
252             =item 1
253              
254             Ref: Int, reference position.
255              
256             =item 2
257              
258             From: Int, the I<from> position.
259              
260             =item 3
261              
262             To: Int, the I<to> position.
263              
264             =back
265              
266             =cut
267              
268             sub align_context {
269 0     0 1 0 my ($self, $ref, $from, $to) = @_;
270 0         0 $from+=0; $to+=0; $ref+=0;
  0         0  
  0         0  
271 0 0 0     0 return if $from == $to and $ref!=0;
272            
273 0         0 my $factory = Bio::Tools::Run::Alignment::Muscle->new();
274 0         0 $factory->quiet(1);
275 0         0 my @seqs = ();
276 0         0 LOCUS: for my $locus (@{$self->loci}){
  0         0  
277             # Get the sequence
278 0         0 my $seq = $locus->context_seq($ref, $from, $to);
279 0 0       0 next LOCUS unless defined $seq;
280 0 0       0 $seq->display_id($locus->id) if defined $locus->id;
281 0         0 push @seqs, $seq;
282             } #LOCUS
283 0 0       0 return unless $#seqs>-1; # Impossible without sequences
284             # small trick to build an alignment, even if there is only one sequence:
285 0 0       0 push @seqs, Bio::Seq->new(-seq=>$seqs[0]->seq, -id=>'dup-seq') unless $#seqs>0;
286 0         0 $self->debug("Aligning context sequences");
287 0         0 return $factory->align(\@seqs);
288             }
289              
290             =head2 fix_strands
291              
292             Fixes the strand of the loci based on the flanking regions, to have all the
293             loci in the group with the same orientation.
294              
295             B<Arguments>
296              
297             =over
298              
299             =item -size I<int>
300              
301             Context size (500 by default)
302              
303             =item -force I<bool (int)>
304              
305             Force the detection, even if it was previously detected.
306              
307             =back
308              
309             =cut
310              
311             sub fix_strands {
312 0     0 1 0 my ($self, @args) = @_;
313 0         0 my ($size, $force) = $self->_rearrange([qw(SIZE FORCE)], @args);
314 0 0 0     0 return if not $force and defined $self->{'_fixed_strands'} and $self->{'_fixed_strands'} == $#{$self->loci};
  0   0     0  
315 0         0 $self->{'_fixed_strands'} = $#{$self->loci};
  0         0  
316 0         0 $self->_load_module('Bio::Polloc::GroupCriteria');
317 0         0 $self->_load_module('Bio::Tools::Run::Alignment::Muscle');
318 0 0       0 return unless $#{$self->loci}>0; # No need to check
  0         0  
319 0   0     0 $size ||= 500;
320            
321 0         0 my $factory = Bio::Tools::Run::Alignment::Muscle->new();
322 0         0 $factory->quiet(1);
323            
324             # Find a suitable reference
325 0         0 my $ref = [undef, undef];
326 0         0 LOCUS: for my $lk (1 .. $#{$self->loci}){
  0         0  
327 0         0 my $ref_test = [
328             Bio::Polloc::GroupCriteria->_build_subseq(
329             $self->loci->[$lk]->seq,
330             $self->loci->[$lk]->from - $size,
331             $self->loci->[$lk]->from),
332             Bio::Polloc::GroupCriteria->_build_subseq(
333             $self->loci->[$lk]->seq,
334             $self->loci->[$lk]->to,
335             $self->loci->[$lk]->to + $size)
336             ];
337 0 0 0     0 if(defined $ref->[0] and defined $ref->[1]){
    0 0        
338             # Longer pair:
339 0 0 0     0 $ref = $ref_test
      0        
      0        
340             if defined $ref_test->[0] and defined $ref_test->[1]
341             and $ref_test->[0]->length >= $ref->[0]->length
342             and $ref_test->[1]->length >= $ref->[1]->length;
343             }elsif(defined $ref->[0] or defined $ref->[1]){
344             # Both sequences defined:
345 0 0 0     0 $ref = $ref_test if defined $ref_test->[0] and defined $ref_test->[1];
346             }else{
347             # At least one sequence defined:
348 0 0 0     0 $ref = $ref_test if defined $ref_test->[0] or defined $ref_test->[1];
349             }
350             }
351 0 0 0     0 unless(defined $ref->[0] or defined $ref->[1]){
352 0         0 $self->debug('Impossible to find a suitable reference');
353 0         0 return;
354             }
355 0 0       0 $ref = defined $ref->[0] ?
    0          
356             ( defined $ref->[1] ?
357             Bio::Seq->new(-seq=>$ref->[0]->seq . ("N"x20) . $ref->[1]->seq)
358             : $ref->[0]
359             ) : $ref->[1];
360            
361 0         0 $ref->id('ref');
362 0         0 $self->loci->[0]->strand('+');
363            
364             # Compare
365 0         0 LOCUS: for my $k (0 .. $#{$self->loci}){
  0         0  
366 0         0 my $tgt = Bio::Polloc::GroupCriteria->_build_subseq(
367             $self->loci->[$k]->seq,
368             $self->loci->[$k]->from-$size,
369             $self->loci->[$k]->to+$size);
370 0 0       0 next LOCUS unless $tgt; # <- This may be way too paranoic!
371 0         0 $tgt->id('tgt');
372 0         0 my $tgtrc = $tgt->revcom;
373 0 0       0 $self->debug("Setting strand for ".$self->loci->[$k]->id) if defined $self->loci->[$k]->id;
374 0         0 my $eval_fun = 'average_percentage_identity';
375             #$eval_fun = 'overall_percentage_identity';
376 0 0       0 if($factory->align([$ref, $tgt])->$eval_fun
377             < $factory->align([$ref,$tgtrc])->$eval_fun){
378 0         0 $self->debug("Assuming negative strand, setting locus orientation");
379 0         0 $self->loci->[$k]->strand('-');
380             }else{
381 0         0 $self->debug("Assuming positive strand, setting locus orientation");
382 0         0 $self->loci->[$k]->strand('+');
383             }
384             } # LOCUS
385             }
386              
387             =head1 INTERNAL METHODS
388              
389             Methods intended to be used only within the scope of Bio::Polloc::*
390              
391             =head2 _initialize
392              
393             =cut
394              
395             sub _initialize {
396 3     3   11 my ($self, @args) = @_;
397 3         19 my($name, $featurename, $genomes) = $self->_rearrange([qw(NAME FEATURENAME GENOMES)], @args);
398 3         13 $self->name($name);
399 3         11 $self->featurename($featurename);
400 3         10 $self->genomes($genomes);
401             }
402              
403             1;