File Coverage

blib/lib/Bio/ToolBox/db_helper/useq.pm
Criterion Covered Total %
statement 96 116 82.7
branch 43 66 65.1
condition 14 20 70.0
subroutine 10 10 100.0
pod 3 3 100.0
total 166 215 77.2


line stmt bran cond sub pod time code
1             package Bio::ToolBox::db_helper::useq;
2              
3             # modules
4             require Exporter;
5 1     1   704 use strict;
  1         3  
  1         36  
6 1     1   5 use Carp;
  1         2  
  1         57  
7 1     1   6 use List::Util qw(min max sum);
  1         2  
  1         85  
8 1     1   7 use Statistics::Lite qw(median);
  1         2  
  1         41  
9 1     1   21 use Bio::ToolBox::db_helper::constants;
  1         2  
  1         67  
10 1     1   7 use Bio::DB::USeq;
  1         1  
  1         1609  
11             our $VERSION = '1.51';
12              
13              
14             # Exported names
15             our @ISA = qw(Exporter);
16             our @EXPORT = qw(
17             collect_useq_scores
18             collect_useq_position_scores
19             open_useq_db
20             );
21              
22             # Hash of USeq chromosomes
23             our %USEQ_CHROMOS;
24             # sometimes user may request a chromosome that's not in the useq file
25             # that could lead to an exception
26             # we will record the chromosomes list in this hash
27             # $USEQ_CHROMOS{useqfile}{chromos}
28             # we also record the chromosome name variant with or without chr prefix
29             # to accommodate different naming conventions
30              
31             # Opened USeq db objects
32             our %OPENED_USEQ;
33             # a cache for opened USeq databases, primarily for collecting scores
34             # caching here is only for local purposes of collecting scores
35             # db_helper also provides caching of db objects but with option to force open in
36             # the case of forking processes - we don't have that here
37              
38             # The true statement
39             1;
40              
41              
42              
43             sub collect_useq_scores {
44            
45             # passed parameters as array ref
46             # chromosome, start, stop, strand, strandedness, method, db, dataset
47 7     7 1 12 my $param = shift;
48            
49             # adjust strand method
50 7         9 my $strand;
51 7 100       29 if ($param->[STND] eq 'antisense') {
    100          
52 1         9 $strand = $param->[STR] * -1;
53             }
54             elsif ($param->[STND] eq 'all') {
55             # Bio::DB::USeq will translate this properly, and collect from
56             # both strands as necessary
57 5         8 $strand = 0;
58             }
59             else {
60             # default
61 1         2 $strand = $param->[STR];
62             }
63            
64             # unlikely there are more than one useq file, but just in case
65 7         10 my @scores;
66 7         19 for (my $d = DATA; $d < scalar @$param; $d++) {
67            
68             # open a new db object
69 7         20 my $useq = _get_useq($param->[$d]);
70            
71             # check chromosome first
72 7 50       24 my $chromo = $USEQ_CHROMOS{$param->[$d]}{$param->[CHR]} or next;
73            
74             # need to collect the scores based on the type of score requested
75 7 100       30 if ($param->[METH] eq 'count') {
    100          
    100          
76             # need to collect features across the region
77 1         9 my $iterator = $useq->get_seq_stream(
78             -seq_id => $chromo,
79             -start => $param->[STRT],
80             -end => $param->[STOP],
81             -strand => $strand,
82             );
83 1 50       332 return unless $iterator;
84            
85             # count each feature
86 1         9 while (my $f = $iterator->next_seq) {
87 437         51849 push @scores, 1;
88             }
89             }
90             elsif ($param->[METH] eq 'ncount') {
91             # need to collect features across the region
92 1         7 my $iterator = $useq->get_seq_stream(
93             -seq_id => $chromo,
94             -start => $param->[STRT],
95             -end => $param->[STOP],
96             -strand => $strand,
97             );
98 1 50       212 return unless $iterator;
99            
100             # store the names
101 1         4 while (my $f = $iterator->next_seq) {
102 437   33     43181 push @scores, $f->display_name || $f->primary_id;
103             # if no display name, a primary_id should automatically be generated
104             }
105             }
106             elsif ($param->[METH] eq 'pcount') {
107             # need to collect features across the region
108 1         8 my $iterator = $useq->get_seq_stream(
109             -seq_id => $chromo,
110             -start => $param->[STRT],
111             -end => $param->[STOP],
112             -strand => $strand,
113             );
114 1 50       232 return unless $iterator;
115            
116             # precisely count each feature
117 1         6 while (my $f = $iterator->next_seq) {
118 437 100 66     46643 push @scores, 1 if
119             ($f->start >= $param->[STRT] and $f->end <= $param->[STOP]);
120             }
121             }
122             else {
123             # everything else is just scores
124 4         22 push @scores, $useq->scores(
125             -seq_id => $chromo,
126             -start => $param->[STRT],
127             -end => $param->[STOP],
128             -strand => $strand,
129             );
130             }
131             }
132            
133 7 50       7127 return wantarray ? @scores : \@scores;
134             }
135              
136              
137              
138             sub collect_useq_position_scores {
139            
140             # passed parameters as array ref
141             # chromosome, start, stop, strand, strandedness, method, db, dataset
142 5     5 1 10 my $param = shift;
143            
144             # adjust strand method
145 5         7 my $strand;
146 5 50       17 if ($param->[STND] eq 'antisense') {
    50          
147 0         0 $strand = $param->[STR] * -1;
148             }
149             elsif ($param->[STND] eq 'all') {
150             # Bio::DB::USeq will translate this properly, and collect from
151             # both strands as necessary
152 0         0 $strand = 0;
153             }
154             else {
155             # default
156 5         11 $strand = $param->[STR];
157             }
158            
159             # unlikely there are more than one useq file, but just in case
160 5         7 my %pos2score;
161 5         19 for (my $d = DATA; $d < scalar @$param; $d++) {
162            
163             # open a new db object
164 5         12 my $useq = _get_useq($param->[$d]);
165            
166             # check chromosome first
167 5 50       17 my $chromo = $USEQ_CHROMOS{$param->[$d]}{$param->[CHR]} or next;
168            
169             # collect the features overlapping the region
170 5         24 my $iterator = $useq->get_seq_stream(
171             -seq_id => $chromo,
172             -start => $param->[STRT],
173             -end => $param->[STOP],
174             -strand => $strand,
175             );
176 5 50       1028 return unless $iterator;
177            
178             # collect each feature
179 5         18 while (my $f = $iterator->next_seq) {
180            
181             # determine position to record
182 244         28942 my $position;
183 244 50       515 if ($f->start == $f->end) {
184             # just one position recorded
185 0         0 $position = $f->start;
186             }
187             else {
188             # calculate the midpoint
189 244         3585 $position = int(
190             ( ($f->start + $f->end) / 2) + 0.5
191             );
192             }
193            
194             # check the position
195             next unless (
196             # want to avoid those whose midpoint are not technically
197             # within the region of interest
198 244 100 100     4001 $position >= $param->[STRT] and $position <= $param->[STOP]
199             );
200            
201             # record the value
202 225 100       554 if ($param->[METH] eq 'count') {
    100          
    100          
203 44         190 $pos2score{$position} += 1;
204             }
205             elsif ($param->[METH] eq 'ncount') {
206 44   50     198 $pos2score{$position} ||= [];
207 44   33     65 push @{ $pos2score{$position} }, $f->display_name ||
  44         108  
208             $f->primary_id;
209             }
210             elsif ($param->[METH] eq 'pcount') {
211 44 100 100     95 $pos2score{$position} += 1 if
212             ($f->start >= $param->[STRT] and $f->end <= $param->[STOP]);
213             }
214             else {
215             # everything else we take the score
216 93         124 push @{ $pos2score{$position} }, $f->score;
  93         350  
217             }
218             }
219             }
220            
221             # combine multiple datapoints at the same position
222 5 100 100     200 if ($param->[METH] eq 'ncount') {
    100          
    50          
    0          
    0          
    0          
    0          
223 1         15 foreach my $position (keys %pos2score) {
224 44         63 my %name2count;
225 44         54 foreach (@{$pos2score{$position}}) { $name2count{$_} += 1 }
  44         69  
  44         110  
226 44         79 $pos2score{$position} = scalar(keys %name2count);
227             }
228             }
229             elsif ($param->[METH] eq 'count' or $param->[METH] eq 'pcount') {
230             # do nothing, these aren't arrays
231             }
232             elsif ($param->[METH] eq 'mean') {
233 2         17 foreach my $position (keys %pos2score) {
234 93         150 $pos2score{$position} = sum( @{$pos2score{$position}} ) /
235 93         113 scalar( @{$pos2score{$position}} );
  93         172  
236             }
237             }
238             elsif ($param->[METH] eq 'median') {
239 0         0 foreach my $position (keys %pos2score) {
240 0         0 $pos2score{$position} = median( @{$pos2score{$position}} );
  0         0  
241             }
242             }
243             elsif ($param->[METH] eq 'min') {
244 0         0 foreach my $position (keys %pos2score) {
245 0         0 $pos2score{$position} = min( @{$pos2score{$position}} );
  0         0  
246             }
247             }
248             elsif ($param->[METH] eq 'max') {
249 0         0 foreach my $position (keys %pos2score) {
250 0         0 $pos2score{$position} = max( @{$pos2score{$position}} );
  0         0  
251             }
252             }
253             elsif ($param->[METH] eq 'sum') {
254 0         0 foreach my $position (keys %pos2score) {
255 0         0 $pos2score{$position} = sum( @{$pos2score{$position}} );
  0         0  
256             }
257             }
258             else {
259             # just take the mean for everything else
260 0         0 foreach my $position (keys %pos2score) {
261 0         0 $pos2score{$position} = sum( @{$pos2score{$position}} ) /
262 0         0 scalar( @{$pos2score{$position}} );
  0         0  
263             }
264             }
265            
266             # return collected data
267 5 50       33 return wantarray ? %pos2score : \%pos2score;
268             }
269              
270              
271              
272             sub open_useq_db {
273            
274             # path
275 2     2 1 4 my $useqfile = shift;
276 2         7 my $path = $useqfile;
277 2         8 $path =~ s/^file://; # clean up file prefix if present
278            
279             # open
280 2         3 my $useq;
281 2         4 eval {
282 2         15 $useq = Bio::DB::USeq->new($path);
283             };
284 2 50       4500 return unless $useq;
285            
286 2         9 return $useq;
287             }
288              
289              
290              
291             ### Internal subroutine for getting the cached USeq object
292             sub _get_useq {
293 12     12   20 my $useqfile = shift;
294            
295 12 100       37 return $OPENED_USEQ{$useqfile} if exists $OPENED_USEQ{$useqfile};
296            
297             # open and cache the USeq object
298 1 50       3 my $useq = open_useq_db($useqfile) or
299             croak " Unable to open USeq file '$useqfile'! $!\n";
300 1         9 $OPENED_USEQ{$useqfile} = $useq;
301            
302             # record the chromosomes and possible variants
303 1         2 $USEQ_CHROMOS{$useqfile} = {};
304 1         3 foreach my $s ($useq->seq_ids) {
305 1         10 $USEQ_CHROMOS{$useqfile}{$s} = $s;
306 1 50       5 if ($s =~ /^chr(.+)$/) {
307 1         4 $USEQ_CHROMOS{$useqfile}{$1} = $s;
308             }
309             else {
310 0         0 $USEQ_CHROMOS{$useqfile}{"chr$s"} = $s;
311             }
312             }
313 1         3 return $useq;
314             }
315              
316              
317              
318             __END__