genevalidator 1.6.12 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +30 -1
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +13 -12
  5. data/Gemfile +4 -1
  6. data/Gemfile.lock +135 -0
  7. data/README.md +104 -122
  8. data/Rakefile +377 -5
  9. data/aux/gv_results.slim +155 -0
  10. data/aux/html_files/css/gv.compiled.min.css +8 -0
  11. data/aux/{files → html_files}/css/src/bootstrap.min.css +0 -0
  12. data/aux/{files → html_files}/css/src/font-awesome.min.css +0 -0
  13. data/aux/{files → html_files}/css/src/style.css +0 -0
  14. data/aux/{files → html_files}/fonts/FontAwesome.otf +0 -0
  15. data/aux/{files → html_files}/fonts/fontawesome-webfont.eot +0 -0
  16. data/aux/{files → html_files}/fonts/fontawesome-webfont.svg +0 -0
  17. data/aux/{files → html_files}/fonts/fontawesome-webfont.ttf +0 -0
  18. data/aux/{files → html_files}/fonts/fontawesome-webfont.woff +0 -0
  19. data/aux/{files → html_files}/img/gene.png +0 -0
  20. data/aux/html_files/js/gv.compiled.min.js +1 -0
  21. data/aux/{files → html_files}/js/src/bootstrap.min.js +0 -0
  22. data/aux/{files → html_files}/js/src/d3.v3.min.js +0 -0
  23. data/aux/{files → html_files}/js/src/jquery-2.1.1.min.js +0 -0
  24. data/aux/{files → html_files}/js/src/jquery.tablesorter.min.js +0 -0
  25. data/aux/{files → html_files}/js/src/plots.js +1 -1
  26. data/aux/{files → html_files}/js/src/script.js +0 -0
  27. data/aux/{files → html_files}/json/.gitkeep +0 -0
  28. data/bin/genevalidator +393 -56
  29. data/exemplar_data/README.md +60 -0
  30. data/{data/mrna_data.fasta → exemplar_data/mrna_data.fa} +1 -1
  31. data/{data/protein_data.fasta → exemplar_data/protein_data.fa} +0 -0
  32. data/genevalidator.gemspec +35 -20
  33. data/install.sh +92 -0
  34. data/lib/genevalidator.rb +171 -56
  35. data/lib/genevalidator/arg_validation.rb +26 -55
  36. data/lib/genevalidator/blast.rb +44 -99
  37. data/lib/genevalidator/clusterization.rb +18 -22
  38. data/lib/genevalidator/exceptions.rb +17 -17
  39. data/lib/genevalidator/ext/array.rb +21 -4
  40. data/lib/genevalidator/get_raw_sequences.rb +32 -31
  41. data/lib/genevalidator/hsp.rb +31 -2
  42. data/lib/genevalidator/json_to_gv_results.rb +38 -122
  43. data/lib/genevalidator/output.rb +158 -172
  44. data/lib/genevalidator/output_files.rb +134 -0
  45. data/lib/genevalidator/pool.rb +2 -5
  46. data/lib/genevalidator/query.rb +1 -1
  47. data/lib/genevalidator/tabular_parser.rb +8 -29
  48. data/lib/genevalidator/validation.rb +48 -90
  49. data/lib/genevalidator/validation_alignment.rb +64 -75
  50. data/lib/genevalidator/validation_blast_reading_frame.rb +13 -9
  51. data/lib/genevalidator/validation_duplication.rb +85 -84
  52. data/lib/genevalidator/validation_gene_merge.rb +46 -35
  53. data/lib/genevalidator/validation_length_cluster.rb +18 -15
  54. data/lib/genevalidator/validation_length_rank.rb +19 -15
  55. data/lib/genevalidator/validation_maker_qi.rb +13 -12
  56. data/lib/genevalidator/validation_open_reading_frame.rb +16 -13
  57. data/lib/genevalidator/validation_report.rb +1 -1
  58. data/lib/genevalidator/validation_test.rb +1 -1
  59. data/lib/genevalidator/version.rb +1 -1
  60. data/test/overall.rb +1 -1
  61. data/test/test_all_validations.rb +36 -24
  62. data/test/test_blast.rb +39 -24
  63. data/test/test_clusterization_2d.rb +4 -4
  64. data/test/test_helper.rb +2 -2
  65. data/test/test_query.rb +16 -20
  66. data/test/test_validation_open_reading_frame.rb +122 -122
  67. data/test/test_validations.rb +12 -10
  68. metadata +94 -79
  69. data/aux/files/css/genevalidator.compiled.min.css +0 -16
  70. data/aux/files/js/genevalidator.compiled.min.js +0 -28
  71. data/aux/json_footer.erb +0 -8
  72. data/aux/json_header.erb +0 -19
  73. data/aux/json_query.erb +0 -15
  74. data/aux/template_footer.erb +0 -8
  75. data/aux/template_header.erb +0 -19
  76. data/aux/template_query.erb +0 -14
  77. data/data/README.md +0 -57
  78. data/data/mrna_data.fasta.blast_tabular +0 -3567
  79. data/data/mrna_data.fasta.blast_tabular.raw_seq +0 -53998
  80. data/data/mrna_data.fasta.blast_tabular.raw_seq.idx +0 -5440
  81. data/data/mrna_data.fasta.blast_xml +0 -39800
  82. data/data/mrna_data.fasta.blast_xml.raw_seq +0 -2554
  83. data/data/mrna_data.fasta.blast_xml.raw_seq.idx +0 -3127
  84. data/data/mrna_data.fasta.json +0 -1
  85. data/data/protein_data.fasta.blast_tabular +0 -3278
  86. data/data/protein_data.fasta.blast_tabular.raw_seq +0 -61295
  87. data/data/protein_data.fasta.blast_tabular.raw_seq.idx +0 -4438
  88. data/data/protein_data.fasta.blast_xml +0 -26228
  89. data/data/protein_data.fasta.blast_xml.raw_seq +0 -9803
  90. data/data/protein_data.fasta.blast_xml.raw_seq.idx +0 -1777
  91. data/data/protein_data.fasta.json +0 -1
@@ -15,7 +15,9 @@ module GeneValidator
15
15
 
16
16
  def initialize(short_header, header, description, frames,
17
17
  expected = :yes)
18
- @short_header, @header, @description = short_header, header, description
18
+ @short_header = short_header
19
+ @header = header
20
+ @description = description
19
21
  @frames = frames
20
22
  @expected = expected
21
23
  @result = validation
@@ -70,7 +72,7 @@ module GeneValidator
70
72
  count_p += 1 if x > 0
71
73
  count_n += 1 if x < 0
72
74
  end
73
- (count_p > 1 || count_n > 1) ? :no : :yes
75
+ count_p > 1 || count_n > 1 ? :no : :yes
74
76
  end
75
77
  end
76
78
 
@@ -78,6 +80,9 @@ module GeneValidator
78
80
  # This class contains the methods necessary for
79
81
  # reading frame validation based on BLAST output
80
82
  class BlastReadingFrameValidation < ValidationTest
83
+ extend Forwardable
84
+ def_delegators GeneValidator, :opt
85
+
81
86
  def initialize(type, prediction, hits = nil)
82
87
  super
83
88
  @short_header = 'ReadingFrame'
@@ -100,8 +105,8 @@ module GeneValidator
100
105
  return @validation_report
101
106
  end
102
107
 
103
- fail NotEnoughHitsError unless hits.length >= 5
104
- fail unless prediction.is_a?(Query) && hits[0].is_a?(Query)
108
+ raise NotEnoughHitsError if hits.length < opt[:min_blast_hits]
109
+ raise unless prediction.is_a?(Query) && hits[0].is_a?(Query)
105
110
 
106
111
  start = Time.now
107
112
 
@@ -116,12 +121,11 @@ module GeneValidator
116
121
  @description, frames)
117
122
  @validation_report.run_time = Time.now - start
118
123
  @validation_report
119
-
120
124
  rescue NotEnoughHitsError
121
- @validation_report = ValidationReport.new('Not enough evidence',
122
- :warning, @short_header,
123
- @header, @description)
124
- rescue
125
+ @validation_report = ValidationReport.new('Not enough evidence',
126
+ :warning, @short_header,
127
+ @header, @description)
128
+ rescue StandardError
125
129
  @validation_report = ValidationReport.new('Unexpected error', :error,
126
130
  @short_header, @header,
127
131
  @description)
@@ -19,7 +19,9 @@ module GeneValidator
19
19
 
20
20
  def initialize(short_header, header, description, pvalue, averages,
21
21
  threshold = 0.05, expected = :yes)
22
- @short_header, @header, @description = short_header, header, description
22
+ @short_header = short_header
23
+ @header = header
24
+ @description = description
23
25
  @pvalue = pvalue
24
26
  @threshold = threshold
25
27
  @result = validation
@@ -36,7 +38,7 @@ module GeneValidator
36
38
 
37
39
  def explain
38
40
  "The Wilcoxon test produced a p-value of #{prettify_evalue(@pvalue)}" \
39
- "#{(@result == :no) ? " (average = #{@average.round(2)})." : '.'}"
41
+ "#{@result == :no ? " (average = #{@average.round(2)})." : '.'}"
40
42
  end
41
43
 
42
44
  def conclude
@@ -50,15 +52,15 @@ module GeneValidator
50
52
  end
51
53
 
52
54
  def print
53
- "#{@pvalue.round(2)}"
55
+ @pvalue.round(2).to_s
54
56
  end
55
57
 
56
58
  def validation
57
- (@pvalue > @threshold) ? :yes : :no
59
+ @pvalue > @threshold ? :yes : :no
58
60
  end
59
61
 
60
62
  def color
61
- (validation == :yes) ? 'success' : 'danger'
63
+ validation == :yes ? 'success' : 'danger'
62
64
  end
63
65
 
64
66
  private
@@ -97,41 +99,33 @@ module GeneValidator
97
99
  @index_file_name = config[:raw_seq_file_index]
98
100
  @raw_seq_file_load = config[:raw_seq_file_load]
99
101
  @db = opt[:db]
100
- @num_threads = opt[:num_threads]
102
+ @num_threads = opt[:mafft_threads]
101
103
  @type = config[:type]
102
104
  end
103
105
 
104
- def in_range?(ranges, idx)
105
- ranges.each do |range|
106
- return (range.member?(idx)) ? true : false
107
- end
108
- false
109
- end
110
-
111
106
  ##
112
107
  # Check duplication in the first n hits
113
108
  # Output:
114
109
  # +DuplicationValidationOutput+ object
115
110
  def run(n = 10)
116
- fail NotEnoughHitsError unless hits.length >= 5
117
- fail unless prediction.is_a?(Query) && !prediction.raw_sequence.nil? &&
118
- hits[0].is_a?(Query)
111
+ raise NotEnoughHitsError if hits.length < opt[:min_blast_hits]
112
+ raise unless prediction.is_a?(Query) && !prediction.raw_sequence.nil? &&
113
+ hits[0].is_a?(Query)
119
114
 
120
115
  start = Time.new
121
116
  # get the first n hits
122
- less_hits = @hits[0..[n - 1, @hits.length].min]
123
- useless_hits = []
117
+ n_hits = [n - 1, @hits.length].min
118
+ less_hits = @hits[0..n_hits]
124
119
  # get raw sequences for less_hits
125
- less_hits.map do |hit|
126
- next unless hit.raw_sequence.nil?
127
- hit.raw_sequence = FetchRawSequences.run(hit.identifier,
128
- hit.accession_no)
129
- useless_hits.push(hit) if hit.raw_sequence.nil?
120
+ less_hits.delete_if do |hit|
121
+ if hit.raw_sequence.nil?
122
+ hit.raw_sequence = FetchRawSequences.run(hit.identifier,
123
+ hit.accession_no)
124
+ end
125
+ hit.raw_sequence.nil? ? true : false
130
126
  end
131
127
 
132
- useless_hits.each { |hit| less_hits.delete(hit) }
133
-
134
- fail NoInternetError if less_hits.length == 0
128
+ raise NoInternetError if less_hits.length.zero?
135
129
 
136
130
  averages = []
137
131
 
@@ -146,62 +140,17 @@ module GeneValidator
146
140
  hit_alignment = hsp.hit_alignment
147
141
  query_alignment = hsp.query_alignment
148
142
  else
149
- # indexing in blast starts from 1
150
- hit_local = hit.raw_sequence[hsp.hit_from - 1..hsp.hit_to - 1]
151
- query_local = prediction.raw_sequence[hsp.match_query_from -
152
- 1..hsp.match_query_to - 1]
153
-
154
- # in case of nucleotide prediction sequence translate into protein
155
- # use translate with reading frame 1 because
156
- # to/from coordinates of the hsp already correspond to the
157
- # reading frame in which the prediction was read to match this hsp
158
- if @type == :nucleotide
159
- s = Bio::Sequence::NA.new(query_local)
160
- query_local = s.translate
161
- end
162
-
163
- # local alignment for hit and query
164
- seqs = [hit_local, query_local]
165
-
166
- begin
167
- options = ['--maxiterate', '1000', '--localpair', '--anysymbol',
168
- '--quiet', '--thread', "#{@num_threads}"]
169
- mafft = Bio::MAFFT.new('mafft', options)
170
-
171
- report = mafft.query_align(seqs)
172
- raw_align = report.alignment
173
- align = []
174
-
175
- raw_align.each { |seq| align.push(seq.to_s) }
176
- hit_alignment = align[0]
177
- query_alignment = align[1]
178
- rescue
179
- raise NoMafftInstallationError
180
- end
143
+ align = find_local_alignment(hit, prediction, hsp)
144
+ hit_alignment = align[0]
145
+ query_alignment = align[1]
181
146
  end
182
147
 
183
- # check multiple coverage
184
-
185
- # for each hsp of the curent hit
186
- # iterate through the alignment and count the matching residues
187
- [*(0..hit_alignment.length - 1)].each do |i|
188
- residue_hit = hit_alignment[i]
189
- residue_query = query_alignment[i]
190
- next if residue_hit == ' ' || residue_hit == '+' ||
191
- residue_hit == '-' || residue_hit != residue_query
192
- # indexing in blast starts from 1
193
- idx_hit = i + (hsp.hit_from - 1) -
194
- hit_alignment[0..i].scan(/-/).length
195
- idx_query = i + (hsp.match_query_from - 1) -
196
- query_alignment[0..i].scan(/-/).length
197
- unless in_range?(ranges_prediction, idx_query)
198
- coverage[idx_hit] += 1
199
- end
200
- end
148
+ coverage = check_multiple_coverage(hit_alignment, query_alignment,
149
+ hsp, coverage, ranges_prediction)
201
150
 
202
- ranges_prediction.push((hsp.match_query_from..hsp.match_query_to))
151
+ ranges_prediction << (hsp.match_query_from..hsp.match_query_to)
203
152
  end
204
- overlap = coverage.reject { |x| x == 0 }
153
+ overlap = coverage.reject(&:zero?)
205
154
  if overlap != []
206
155
  averages.push((overlap.inject(:+) / (overlap.length + 0.0)).round(2))
207
156
  end
@@ -225,7 +174,6 @@ module GeneValidator
225
174
  averages)
226
175
  @run_time = Time.now - start
227
176
  @validation_report
228
-
229
177
  rescue NotEnoughHitsError
230
178
  @validation_report = ValidationReport.new('Not enough evidence', :warning,
231
179
  @short_header, @header,
@@ -240,22 +188,75 @@ module GeneValidator
240
188
  @short_header, @header,
241
189
  @description)
242
190
  @validation_report.errors.push NoInternetError
243
- rescue
191
+ rescue StandardError
244
192
  @validation_report = ValidationReport.new('Unexpected error', :error,
245
193
  @short_header, @header,
246
194
  @description)
247
195
  @validation_report.errors.push 'Unexpected Error'
248
196
  end
249
197
 
198
+ # Only run if the BLAST output does not contain hit alignmment
199
+ def find_local_alignment(hit, prediction, hsp)
200
+ # indexing in blast starts from 1
201
+ hit_local = hit.raw_sequence[hsp.hit_from - 1..hsp.hit_to - 1]
202
+ query_local = prediction.raw_sequence[hsp.match_query_from -
203
+ 1..hsp.match_query_to - 1]
204
+
205
+ # in case of nucleotide prediction sequence translate into protein
206
+ # use translate with reading frame 1 because
207
+ # to/from coordinates of the hsp already correspond to the
208
+ # reading frame in which the prediction was read to match this hsp
209
+ if @type == :nucleotide
210
+ s = Bio::Sequence::NA.new(query_local)
211
+ query_local = s.translate
212
+ end
213
+
214
+ opt = ['--maxiterate', '1000', '--localpair', '--anysymbol', '--quiet',
215
+ '--thread', @num_threads.to_s]
216
+ mafft = Bio::MAFFT.new('mafft', opt)
217
+
218
+ # local alignment for hit and query
219
+ seqs = [hit_local, query_local]
220
+ report = mafft.query_align(seqs)
221
+ report.alignment.map(&:to_s)
222
+ rescue StandardError
223
+ raise NoMafftInstallationError
224
+ end
225
+
226
+ def check_multiple_coverage(hit_alignment, query_alignment, hsp, coverage,
227
+ ranges_prediction)
228
+ # for each hsp of the curent hit
229
+ # iterate through the alignment and count the matching residues
230
+ [*(0..hit_alignment.length - 1)].each do |i|
231
+ residue_hit = hit_alignment[i]
232
+ residue_query = query_alignment[i]
233
+ next if [' ', '+', '-'].include?(residue_hit)
234
+ next if residue_hit != residue_query
235
+ # indexing in blast starts from 1
236
+ idx_hit = i + (hsp.hit_from - 1) -
237
+ hit_alignment[0..i].scan(/-/).length
238
+ idx_query = i + (hsp.match_query_from - 1) -
239
+ query_alignment[0..i].scan(/-/).length
240
+ coverage[idx_hit] += 1 unless in_range?(ranges_prediction, idx_query)
241
+ end
242
+ coverage
243
+ end
244
+
245
+ def in_range?(ranges, idx)
246
+ ranges.each { |range| return true if range.member?(idx) }
247
+ false
248
+ end
249
+
250
250
  ##
251
251
  # wilcox test implementation from statsample ruby gem
252
252
  # many thanks to Claudio for helping us with the implementation!
253
253
  def wilcox_test(averages)
254
- wilcox = Statsample::Test.wilcoxon_signed_rank(Daru::Vector.new(averages),
255
- Daru::Vector.new(Array.new(averages.length,
256
- 1)))
254
+ wilcox = Statsample::Test.wilcoxon_signed_rank(
255
+ Daru::Vector.new(averages),
256
+ Daru::Vector.new(Array.new(averages.length, 1))
257
+ )
257
258
 
258
- (averages.length < 15) ? wilcox.probability_exact : wilcox.probability_z
259
+ averages.length < 15 ? wilcox.probability_exact : wilcox.probability_z
259
260
  end
260
261
  end
261
262
  end
@@ -22,7 +22,9 @@ module GeneValidator
22
22
 
23
23
  def initialize(short_header, header, description, slope, unimodality,
24
24
  expected = :no)
25
- @short_header, @header, @description = short_header, header, description
25
+ @short_header = short_header
26
+ @header = header
27
+ @description = description
26
28
  @slope = slope.round(1)
27
29
  @slope = @slope.abs if @slope == -0.0
28
30
  @unimodality = unimodality
@@ -56,30 +58,30 @@ module GeneValidator
56
58
  if @unimodality
57
59
  'This suggest that the query sequence represents a single gene.'
58
60
  else
59
- diff = (@result == :yes) ? ' within' : ' outside'
61
+ diff = @result == :yes ? ' within' : ' outside'
60
62
  t = "This slope is #{diff} our empirically calculated thresholds" \
61
63
  ' (0.4 and 1.2).'
62
- if @result == :yes
63
- t << ' This suggests the query contains sequence from two or more' \
64
- ' different genes.'
65
- else
66
- t << ' There is no evidence that the query contains sequence from' \
67
- ' multiple genes.'
68
- end
64
+ t << if @result == :yes
65
+ ' This suggests the query contains sequence from two or more' \
66
+ ' different genes.'
67
+ else
68
+ ' There is no evidence that the query contains sequence from' \
69
+ ' multiple genes.'
70
+ end
69
71
  t
70
72
  end
71
73
  end
72
74
 
73
75
  def print
74
- (@slope.nan?) ? 'Inf' : "#{@slope}"
76
+ @slope.nan? ? 'Inf' : @slope.to_s
75
77
  end
76
78
 
77
79
  def validation
78
- (@slope > threshold_down && @slope < threshold_up) ? :yes : :no
80
+ @slope > threshold_down && @slope < threshold_up ? :yes : :no
79
81
  end
80
82
 
81
83
  def color
82
- (validation == :no) ? 'success' : 'danger'
84
+ validation == :no ? 'success' : 'danger'
83
85
  end
84
86
  end
85
87
 
@@ -88,6 +90,9 @@ module GeneValidator
88
90
  # checking whether there is evidence that the
89
91
  # prediction is a merge of multiple genes
90
92
  class GeneMergeValidation < ValidationTest
93
+ extend Forwardable
94
+ def_delegators GeneValidator, :opt
95
+
91
96
  attr_reader :prediction
92
97
  attr_reader :hits
93
98
 
@@ -113,18 +118,20 @@ module GeneValidator
113
118
  # Output:
114
119
  # +GeneMergeValidationOutput+ object
115
120
  def run
116
- fail NotEnoughHitsError unless hits.length >= 5
117
- fail unless prediction.is_a?(Query) && hits[0].is_a?(Query)
121
+ raise NotEnoughHitsError if hits.length < opt[:min_blast_hits]
122
+ raise unless prediction.is_a?(Query) && hits[0].is_a?(Query)
118
123
 
119
124
  start = Time.now
120
125
 
121
- pairs = hits.map { |hit| Pair.new(hit.hsp_list.map{ |hsp| hsp.match_query_from }.min,
122
- hit.hsp_list.map{ |hsp| hsp.match_query_to }.max) }
126
+ pairs = hits.map do |hit|
127
+ Pair.new(hit.hsp_list.map(&:match_query_from).min,
128
+ hit.hsp_list.map(&:match_query_to).max)
129
+ end
123
130
  xx_0 = pairs.map(&:x)
124
131
  yy_0 = pairs.map(&:y)
125
132
 
126
133
  # minimum start shoud be at 'boundary' residues
127
- xx = xx_0.map { |x| (x < @boundary) ? @boundary : x }
134
+ xx = xx_0.map { |x| x < @boundary ? @boundary : x }
128
135
 
129
136
  # maximum end should be at length - 'boundary' residues
130
137
  yy = yy_0.map do |y|
@@ -151,23 +158,22 @@ module GeneValidator
151
158
  @validation_report = GeneMergeValidationOutput.new(@short_header, @header,
152
159
  @description, lm_slope,
153
160
  unimodality)
154
- if unimodality
155
- plot1 = plot_2d_start_from
156
- else
157
- plot1 = plot_2d_start_from(lm_slope, y_intercept)
158
- end
161
+ plot1 = if unimodality
162
+ plot_2d_start_from
163
+ else
164
+ plot_2d_start_from(lm_slope, y_intercept)
165
+ end
159
166
 
160
167
  @validation_report.plot_files.push(plot1)
161
168
  plot2 = plot_matched_regions
162
169
  @validation_report.plot_files.push(plot2)
163
170
  @validation_report.run_time = Time.now - start
164
171
  @validation_report
165
-
166
172
  rescue NotEnoughHitsError
167
173
  @validation_report = ValidationReport.new('Not enough evidence', :warning,
168
174
  @short_header, @header,
169
175
  @description)
170
- rescue
176
+ rescue StandardError
171
177
  @validation_report = ValidationReport.new('Unexpected error', :error,
172
178
  @short_header, @header,
173
179
  @description)
@@ -186,18 +192,21 @@ module GeneValidator
186
192
 
187
193
  hits_less = hits[0..[no_lines, hits.length - 1].min]
188
194
 
189
- data = hits_less.each_with_index.map { |hit, i|
195
+ data = hits_less.each_with_index.map do |hit, i|
190
196
  { 'y' => i,
191
197
  'start' => hit.hsp_list.map(&:match_query_from).min,
192
198
  'stop' => hit.hsp_list.map(&:match_query_to).max,
193
199
  'color' => 'black',
194
- 'dotted' => 'true' } }.flatten +
195
- hits_less.each_with_index.map { |hit, i|
196
- hit.hsp_list.map { |hsp|
197
- { 'y' => i,
198
- 'start' => hsp.match_query_from,
199
- 'stop' => hsp.match_query_to,
200
- 'color' => 'orange' } } }.flatten
200
+ 'dotted' => 'true' }
201
+ end .flatten +
202
+ hits_less.each_with_index.map do |hit, i|
203
+ hit.hsp_list.map do |hsp|
204
+ { 'y' => i,
205
+ 'start' => hsp.match_query_from,
206
+ 'stop' => hsp.match_query_to,
207
+ 'color' => 'orange' }
208
+ end
209
+ end .flatten
201
210
 
202
211
  Plot.new(data,
203
212
  :lines,
@@ -223,9 +232,11 @@ module GeneValidator
223
232
  hit.hsp_list.map(&:match_query_to).max)
224
233
  end
225
234
 
226
- data = hits.map { |hit| { 'x' => hit.hsp_list.map(&:match_query_from).min,
227
- 'y' => hit.hsp_list.map(&:match_query_to).max,
228
- 'color' => 'red' } }
235
+ data = hits.map do |hit|
236
+ { 'x' => hit.hsp_list.map(&:match_query_from).min,
237
+ 'y' => hit.hsp_list.map(&:match_query_to).max,
238
+ 'color' => 'red' }
239
+ end
229
240
 
230
241
  Plot.new(data,
231
242
  :scatter,