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   584 use strict;
  1         29  
  1         41  
6 1     1   5 use Carp;
  1         1  
  1         57  
7 1     1   5 use List::Util qw(min max sum);
  1         2  
  1         87  
8 1     1   6 use Statistics::Lite qw(median);
  1         2  
  1         34  
9 1     1   4 use Bio::ToolBox::db_helper::constants;
  1         1  
  1         53  
10 1     1   6 use Bio::DB::USeq;
  1         1  
  1         1150  
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 10 my $param = shift;
48            
49             # adjust strand method
50 7         10 my $strand;
51 7 100       16 if ($param->[STND] eq 'antisense') {
    100          
52 1         4 $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         3 $strand = $param->[STR];
62             }
63            
64             # unlikely there are more than one useq file, but just in case
65 7         8 my @scores;
66 7         21 for (my $d = DATA; $d < scalar @$param; $d++) {
67            
68             # open a new db object
69 7         13 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       23 if ($param->[METH] eq 'count') {
    100          
    100          
76             # need to collect features across the region
77 1         6 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       229 return unless $iterator;
84            
85             # count each feature
86 1         4 while (my $f = $iterator->next_seq) {
87 437         37727 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       194 return unless $iterator;
99            
100             # store the names
101 1         3 while (my $f = $iterator->next_seq) {
102 437   33     30194 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       207 return unless $iterator;
115            
116             # precisely count each feature
117 1         4 while (my $f = $iterator->next_seq) {
118 437 100 66     33353 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         16 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       5116 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         8 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         10 $strand = $param->[STR];
157             }
158            
159             # unlikely there are more than one useq file, but just in case
160 5         6 my %pos2score;
161 5         12 for (my $d = DATA; $d < scalar @$param; $d++) {
162            
163             # open a new db object
164 5         15 my $useq = _get_useq($param->[$d]);
165            
166             # check chromosome first
167 5 50       21 my $chromo = $USEQ_CHROMOS{$param->[$d]}{$param->[CHR]} or next;
168            
169             # collect the features overlapping the region
170 5         27 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       1034 return unless $iterator;
177            
178             # collect each feature
179 5         12 while (my $f = $iterator->next_seq) {
180            
181             # determine position to record
182 244         21233 my $position;
183 244 50       376 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         2650 $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     3000 $position >= $param->[STRT] and $position <= $param->[STOP]
199             );
200            
201             # record the value
202 225 100       413 if ($param->[METH] eq 'count') {
    100          
    100          
203 44         154 $pos2score{$position} += 1;
204             }
205             elsif ($param->[METH] eq 'ncount') {
206 44   50     150 $pos2score{$position} ||= [];
207 44   33     44 push @{ $pos2score{$position} }, $f->display_name ||
  44         76  
208             $f->primary_id;
209             }
210             elsif ($param->[METH] eq 'pcount') {
211 44 100 100     57 $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         90 push @{ $pos2score{$position} }, $f->score;
  93         212  
217             }
218             }
219             }
220            
221             # combine multiple datapoints at the same position
222 5 100 100     128 if ($param->[METH] eq 'ncount') {
    100          
    50          
    0          
    0          
    0          
    0          
223 1         7 foreach my $position (keys %pos2score) {
224 44         38 my %name2count;
225 44         42 foreach (@{$pos2score{$position}}) { $name2count{$_} += 1 }
  44         48  
  44         61  
226 44         57 $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         14 foreach my $position (keys %pos2score) {
234 93         110 $pos2score{$position} = sum( @{$pos2score{$position}} ) /
235 93         80 scalar( @{$pos2score{$position}} );
  93         119  
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       31 return wantarray ? %pos2score : \%pos2score;
268             }
269              
270              
271              
272             sub open_useq_db {
273            
274             # path
275 2     2 1 3 my $useqfile = shift;
276 2         3 my $path = $useqfile;
277 2         6 $path =~ s/^file://; # clean up file prefix if present
278            
279             # open
280 2         3 my $useq;
281 2         3 eval {
282 2         11 $useq = Bio::DB::USeq->new($path);
283             };
284 2 50       3442 return unless $useq;
285            
286 2         6 return $useq;
287             }
288              
289              
290              
291             ### Internal subroutine for getting the cached USeq object
292             sub _get_useq {
293 12     12   24 my $useqfile = shift;
294            
295 12 100       38 return $OPENED_USEQ{$useqfile} if exists $OPENED_USEQ{$useqfile};
296            
297             # open and cache the USeq object
298 1 50       4 my $useq = open_useq_db($useqfile) or
299             croak " Unable to open USeq file '$useqfile'! $!\n";
300 1         3 $OPENED_USEQ{$useqfile} = $useq;
301            
302             # record the chromosomes and possible variants
303 1         3 $USEQ_CHROMOS{$useqfile} = {};
304 1         3 foreach my $s ($useq->seq_ids) {
305 1         9 $USEQ_CHROMOS{$useqfile}{$s} = $s;
306 1 50       4 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         2 return $useq;
314             }
315              
316              
317              
318             __END__