]> git.donarmstrong.com Git - biopieces.git/blob - code_ruby/lib/maasha/usearch.rb
fixing usearch upgrade
[biopieces.git] / code_ruby / lib / maasha / usearch.rb
1 # Copyright (C) 2011 Martin A. Hansen.
2
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
7
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17 # http://www.gnu.org/copyleft/gpl.html
18
19 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
20
21 # This software is part of the Biopieces framework (www.biopieces.org).
22
23 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
24
25 require 'maasha/fasta'
26
27 # Error class for all exceptions to do with Usearch.
28 class UsearchError < StandardError; end
29
30 class Usearch
31   include Enumerable
32
33   def initialize(infile, outfile, options)
34     @infile  = infile
35     @outfile = outfile
36     @options = options
37     @command = []
38
39     raise UsearchError, %{Empty input file -> "#{@infile}"} if File.size(@infile) == 0
40   end
41
42   # Method that calls Usearch sorting for sorting a FASTA file
43   # according to decending sequence length.
44   def sortbylength
45     # usearch -sortbylength seqs.fasta -output seqs_sorted.fasta -minseqlength 64
46     @command << "usearch"
47     @command << "-sortbylength #{@infile}"
48     @command << "-output #{@infile}.sort"
49
50                 execute
51
52     File.rename "#{@infile}.sort", @infile
53   end
54
55   # Method that calls Usearch sorting for sorting a FASTA file
56   # according to cluster size.
57   def sortbysize
58     # usearch -sortbysize seqs.fasta -output seqs_sorted.fasta -minsize 4
59     @command << "usearch"
60     @command << "-sortbysize #{@infile}"
61     @command << "-output #{@infile}.sort"
62
63                 execute
64
65     File.rename "#{@infile}.sort", @infile
66   end
67
68   # Method to execute cluster_fast.
69   def cluster_fast
70     # usearch -cluster_fast query.fasta -id 0.9 -centroids nr.fasta -uc clusters.uc
71     @command << "usearch"
72     @command << "-cluster_fast #{@infile}"
73     @command << "-id #{@options[:identity]}"
74     @command << "-uc #{@outfile}"
75     @command << "-threads #{@options[:cpus]}"
76
77     execute
78   end
79
80   # Method to execute cluster_smallmem.
81   # NB sequences must be sorted with sortbylength or sortbysize.
82   def cluster_smallmem
83     # usearch -cluster_smallmem query.fasta -id 0.9 -centroids nr.fasta -uc clusters.uc
84     @command << "usearch"
85     @command << "-cluster_smallmem #{@infile}"
86     @command << "-id #{@options[:identity]}"
87     @command << "-uc #{@outfile}"
88     @command << "-strand both" if @options[:comp]
89
90     execute
91   end
92
93   # Method to execute database local search.
94   def usearch_local
95     # usearch -usearch_local query.fasta -db proteins.udb -id 0.8 -alnout results.aln
96     # usearch -usearch_local query.fasta -db ESTs.fasta -id 0.9 -evalue 1e-6 -blast6out results.m8 -strand plus -maxaccepts 8 -maxrejects 256
97     
98     @command << "usearch"
99     @command << "-usearch_local #{@infile}"
100     @command << "-db #{@options[:database]}"
101     @command << "-blast6out #{@outfile}"
102     @command << "-strand both"
103     @command << "-id #{@options[:identity]}"  if @options[:identity]
104     @command << "-evalue #{@options[:e_val]}" if @options[:e_val]
105     @command << "-maxaccepts #{@options[:maxaccepts]}"
106     @command << "-threads #{@options[:cpus]}"
107
108     execute
109   end
110
111   # Method to execute database global search.
112   def usearch_global
113     # usearch -usearch_global query.fasta -db proteins.udb -id 0.8 -alnout results.aln
114     # usearch -usearch_global query.fasta -db ESTs.fasta -id 0.9 -blast6out results.m8 -strand plus -maxaccepts 8 -maxrejects 256
115
116     @command << "usearch"
117     @command << "-usearch_global #{@infile}"
118     @command << "-db #{@options[:database]}"
119     @command << "-blast6out #{@outfile}"
120     @command << "-strand both"
121     @command << "-id #{@options[:identity]}"
122     @command << "-evalue #{@options[:e_val]}" if @options[:e_val]
123     @command << "-maxaccepts #{@options[:maxaccepts]}"
124     @command << "-threads #{@options[:cpus]}"
125
126     execute
127   end
128
129   # Method to execute uchime chimera detection.
130   def uchime
131     @command << "usearch --uchime #{@infile} --db #{@options[:database]} --uchimeout #{@outfile}"
132
133     execute
134   end
135
136   # Method to execute ustar alignment.
137   def ustar
138     command = %Q{grep "^[SH]" #{@outfile} > #{@outfile}.sub}
139     system(command)
140     raise "Command failed: #{command}" unless $?.success?
141
142     File.rename "#{@outfile}.sub", @outfile
143
144     @command << "usearch --uc2fastax #{@outfile} --input #{@infile} --output #{@infile}.sub"
145
146     execute
147
148     @command << "usearch --staralign #{@infile}.sub --output #{@outfile}"
149
150     execute
151
152     File.delete "#{@infile}.sub"
153   end
154
155         # Method to parse a Uclust .uc file and for each line of data
156         # yield a Biopiece record.
157   def each_cluster
158     record = {}
159
160     File.open(@outfile, "r") do |ios|
161       ios.each_line do |line|
162         if line !~ /^#/
163           fields = line.chomp.split("\t")
164
165           next if fields[0] == 'C'
166
167           record[:TYPE]     = fields[0]
168           record[:CLUSTER]  = fields[1].to_i
169           record[:IDENT]    = fields[3].to_f
170           record[:Q_ID]     = fields[8]
171
172           yield record
173         end
174       end
175     end
176
177     self # conventionally
178   end
179
180         # Method to parse a Useach user defined tabular file and for each line of data
181         # yield a Biopiece record.
182   def each_hit
183     record = {}
184
185     File.open(@outfile, "r") do |ios|
186       ios.each_line do |line|
187         fields = line.chomp.split("\t")
188         record[:REC_TYPE]   = "USEARCH"
189         record[:Q_ID]       = fields[0]
190         record[:S_ID]       = fields[1]
191         record[:IDENT]      = fields[2].to_f
192         record[:ALIGN_LEN]  = fields[3].to_i
193         record[:MISMATCHES] = fields[4].to_i
194         record[:GAPS]       = fields[5].to_i
195         record[:Q_BEG]      = fields[6].to_i - 1
196         record[:Q_END]      = fields[7].to_i - 1
197         record[:S_BEG]      = fields[8].to_i - 1
198         record[:S_END]      = fields[9].to_i - 1
199         record[:E_VAL]      = fields[10] == '*' ? '*' : fields[10].to_f
200         record[:SCORE]      = fields[11] == '*' ? '*' : fields[11].to_f
201         record[:STRAND]     = record[:S_BEG].to_i < record[:S_END].to_i ? '+' : '-'
202
203         record[:S_BEG], record[:S_END] = record[:S_END], record[:S_BEG] if record[:STRAND] == '-'
204
205         yield record
206       end
207     end
208
209     self # conventionally
210   end
211
212   # Method to parse a FASTA file with Ustar alignments and for each alignment
213   # yield an Align object.
214   def each_alignment
215     old_cluster = 0
216     entries     = []
217
218     Fasta.open(@outfile, "r") do |ios|
219       ios.each do |entry|
220         entry.seq.tr!('.', '-')   # Replace . with - in Ustar alignments.
221         cluster, identity, name = entry.seq_name.split('|')
222         cluster = cluster.to_i
223
224         if cluster == old_cluster
225           entries << entry
226         else
227           fix_alignment(entries)
228
229           yield Align.new(entries)
230
231           old_cluster = cluster
232           entries     = []
233           entries << entry
234         end
235       end
236
237       yield Align.new(entries) unless entries.empty?
238     end
239
240     self # conventionally
241   end
242
243   private
244
245   # Method that fixed Ustar bug resulting in alignments with uneven 
246   # sequence length.
247   def fix_alignment(entries)
248     if entries.size > 1
249       min, max = entries.minmax { |a, b| a.length <=> b.length } 
250
251       if min.length != max.length
252         entries.each do |entry|
253           entry.seq << '-' * (max.length - entry.length)
254         end
255       end
256     end
257   end
258
259         # Method to execute a command using a system() call.
260         # The command is composed of bits from the @command variable.
261         def execute
262                 @command << "--quiet" unless @options[:verbose]
263                 command = @command.join(" ")
264     $stderr.puts "Running command: #{command}" if @options[:verbose]
265     system(command)
266     raise "Command failed: #{command}" unless $?.success?
267
268                 @command = []
269         end
270 end
271
272 __END__
273