wrnap 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a49216040e651785e22f2fe010a08ad6ace0800
4
- data.tar.gz: beb5164b299e498a0a0c1d59ca579efb723aef46
3
+ metadata.gz: b2cc53a30fd53e8dc635b9a6f0e6b9dfc985eefb
4
+ data.tar.gz: ce9692b9164e77fac7b6d4e56ed6398500c19e71
5
5
  SHA512:
6
- metadata.gz: b4262912d96fa147269069032324e0319cc8deb5391dd56376acdea9165c9cc48ddf3d3755c221d9bdc1d503d6bda8840962b5437dc7522a1244571ed089d017
7
- data.tar.gz: 29c9eb3b9c20d32c6cdda08a6629fbf5917bfb482a3b76e2ac1f9150ecc54c799320789387a9d97fc4f8b35810fc4877d0e1667fc3166834a548b7f43d0f06eb
6
+ metadata.gz: 6e5c9b208ddb20f3d9377a40bfa6e5467a35c9012ddd0a0e38bcb47f082de147bd82df8ef7040a9dad2910f87f6bcc18df79b0f187a142f5ba99afa6bcc76d17
7
+ data.tar.gz: cf74fe53fde6cbdf70a7430d6dee2e56cbbcd12ad64b9a9e6c11a68e279b7a46657ffd2fe1b5d5ed9d813e223016f27479a638b72e8f05537ed1f0762cb22f92
@@ -7,7 +7,7 @@ module Wrnap
7
7
  LOCAL_END = /\*\[\s*\d+\s*\]\*/
8
8
 
9
9
  class << self
10
- def parse_file(file)
10
+ def load_all(file)
11
11
  output = File.exist?(file) ? File.read(file) : file
12
12
 
13
13
  if output =~ /No hits detected that satisfy reporting thresholds/
@@ -17,7 +17,7 @@ module Wrnap
17
17
  gsub(/^(.*\n)*Hit alignments:\n/, "").
18
18
  gsub(/Internal CM pipeline statistics summary:\n(.*\n)*$/, "").
19
19
  strip.split(?\n).reject(&:empty?).each_slice(10).map { |lines| parse_hit(lines.join(?\n)) }.compact
20
- end
20
+ end.wrnap
21
21
  end
22
22
 
23
23
  def parse_hit(output)
@@ -4,10 +4,21 @@ module Wrnap
4
4
  class << self
5
5
  def load_all(file)
6
6
  entries = Bio::Stockholm::Reader.parse_from_file(file)[0]
7
- sequences = entries.records.map(&:last).map(&:sequence)
8
- structure = dot_bracket_from_stockholm(entries.gc_features["SS_cons"])
7
+ structure = consensus_structure_from_file(file)
9
8
 
10
- sequences.map { |sequence| fit_structure_to_sequence(sequence, structure) }
9
+ entries.records.map do |label, record|
10
+ fit_structure_to_sequence(record.sequence, structure).tap do |rna|
11
+ rna.comment = if !record.gs_features.nil? && record.gs_features.is_a?(Hash)
12
+ record.gs_features["AC"].split(/[\/-]/).join(" ")
13
+ else
14
+ label.split(/[\/-]/).join(" ")
15
+ end
16
+ end
17
+ end.wrnap
18
+ end
19
+
20
+ def consensus_structure_from_file(file)
21
+ dot_bracket_from_stockholm(Bio::Stockholm::Reader.parse_from_file(file)[0].gc_features["SS_cons"])
11
22
  end
12
23
 
13
24
  def dot_bracket_from_stockholm(structure)
@@ -19,16 +19,18 @@ module Wrnap
19
19
  def sequence_from_entrez(id, position, window)
20
20
  Wrnap.debugger { "Retrieving sequence from Entrez: using nuccore DB (id: #{id}, seq_start: #{position + window.min}, seq_stop: #{position + window.max})" }
21
21
  Wrnap.debugger { "> True starting position: #{position} with window #{window.min} to #{window.max}" }
22
-
22
+
23
23
  fasta = ::Entrez.EFetch("nuccore", {
24
24
  id: id,
25
25
  seq_start: position + window.min,
26
26
  seq_stop: position + window.max,
27
27
  retmode: :fasta,
28
28
  rettype: :text
29
- }).response.body
29
+ })
30
+
31
+ Wrnap.debugger { fasta }
30
32
 
31
- Bio::FastaFormat.new(fasta)
33
+ Bio::FastaFormat.new(fasta.response.body)
32
34
  end
33
35
  end
34
36
  end
@@ -10,6 +10,10 @@ module Wrnap
10
10
  def exec_exists?(name)
11
11
  !%x[which RNA#{name.to_s.downcase}].empty? || !%x[which #{name.to_s.downcase}].empty?
12
12
  end
13
+
14
+ def exec_name
15
+ executable_name.respond_to?(:call) ? executable_name[self] : executable_name
16
+ end
13
17
 
14
18
  def run(*data)
15
19
  flags = data.length > 1 && data.last.is_a?(Hash) ? data.pop : {}
@@ -40,13 +44,23 @@ module Wrnap
40
44
  end
41
45
 
42
46
  def pre_run_check
43
- if %x[which #{exec_name}].empty?
47
+ valid_to_run = if self.class.instance_variable_get(:@pre_run_checked)
48
+ self.class.instance_variable_get(:@valid_to_run)
49
+ else
50
+ Wrnap.debugger { "Checking existence of executable %s." % exec_name }
51
+ self.class.module_eval do
52
+ @pre_run_checked = true
53
+ @valid_to_run = !%x[which #{exec_name}].empty?
54
+ end
55
+ end
56
+
57
+ unless valid_to_run
44
58
  raise RuntimeError.new("#{exec_name} is not defined on this machine")
45
59
  end
46
60
  end
47
61
 
48
62
  def exec_name
49
- executable_name.respond_to?(:call) ? executable_name[self] : executable_name
63
+ self.class.exec_name
50
64
  end
51
65
 
52
66
  def recursively_merge_flags(flags)
@@ -2,10 +2,10 @@ module Wrnap
2
2
  module Graphing
3
3
  module R
4
4
  class << self
5
- def graph(&block)
5
+ def graph(width: 8, height: 6, &block)
6
6
  if const_defined?(:RinRuby)
7
7
  begin
8
- (yield (r_instance = RinRuby.new(false))).tap { r_instance.close }
8
+ generate_graph(r_instance = RinRuby.new(false), width, height, &block).tap { r_instance.close }
9
9
  rescue RuntimeError => e
10
10
  raise unless e.message == "Unsupported data type on R's end"
11
11
  end
@@ -23,11 +23,6 @@ module Wrnap
23
23
  y_range = Range.new(y_points.map(&:min).min.floor, y_points.map(&:max).max.ceil)
24
24
 
25
25
  graph do |r|
26
- r.eval("%s('%s', 6, 6)" % [
27
- writing_file?(filename) ? filetype(filename) : "quartz",
28
- writing_file?(filename) ? filename : "Graph",
29
- ])
30
-
31
26
  r.assign("legend.titles", data.each_with_index.map { |hash, index| hash[:legend] || "Line #{index + 1}" })
32
27
  r.eval("line.colors <- rainbow(%d)" % data.size)
33
28
  r.eval("plot(0, 0, type = 'n', cex = .75, cex.axis = .9, xlab = '', ylab = '', xlim = c(%d, %d), ylim = c(%d, %d))" % [
@@ -73,8 +68,6 @@ module Wrnap
73
68
  )
74
69
  STR
75
70
  end
76
-
77
- r.eval("dev.off()") if writing_file?(filename)
78
71
  end
79
72
  end
80
73
 
@@ -115,7 +108,6 @@ module Wrnap
115
108
  end
116
109
  end
117
110
 
118
-
119
111
  overlay(
120
112
  formatted_data,
121
113
  title: title,
@@ -135,11 +127,6 @@ module Wrnap
135
127
  r.assign("histogram.data", data)
136
128
  r.assign("histogram.breaks", breaks)
137
129
 
138
- r.eval("%s('%s', 6, 6)" % [
139
- writing_file?(filename) ? filetype(filename) : "quartz",
140
- writing_file?(filename) ? filename : "Graph",
141
- ])
142
-
143
130
  r.eval <<-STR
144
131
  hist(
145
132
  histogram.data,
@@ -154,8 +141,6 @@ module Wrnap
154
141
  STR
155
142
 
156
143
  r.eval("abline(v = #{x_arrow}, lty = 'dashed')") if x_arrow
157
-
158
- r.eval("dev.off()") if writing_file?(filename)
159
144
  end
160
145
  end
161
146
 
@@ -179,26 +164,22 @@ module Wrnap
179
164
  x = matrix.x,
180
165
  index1 = F
181
166
  )
167
+
168
+ filtered.values <- Filter(function(i) { is.finite(i) & i != 0 }, matrix.x)
169
+ print(apply(as.matrix(matrix.data), 2, rev))
170
+ print(c(sort(filtered.values)[2], max(filtered.values)))
171
+
172
+ image(
173
+ x = 1:max(c(dim(matrix.data)[[1]], dim(matrix.data)[[2]])),
174
+ y = 1:max(c(dim(matrix.data)[[1]], dim(matrix.data)[[2]])),
175
+ z = as.matrix(matrix.data),
176
+ col = rev(heat.colors(#{num_colors})),
177
+ zlim = #{forced_square ? "c(sort(filtered.values)[2], max(filtered.values))" : "c(min(filtered.values), max(filtered.values))"},
178
+ xlab = "#{x_label} (1-indexed)",
179
+ ylab = "#{y_label} (1-indexed)"
180
+ )
181
+ title(#{expressionify(title || "Matrix Heatmap")})
182
182
  STR
183
-
184
- generate_graph("Heatmap") do
185
- <<-STR
186
- filtered.values <- Filter(function(i) { is.finite(i) & i != 0 }, matrix.x)
187
- print(apply(as.matrix(matrix.data), 2, rev))
188
- print(c(sort(filtered.values)[2], max(filtered.values)))
189
-
190
- image(
191
- x = 1:max(c(dim(matrix.data)[[1]], dim(matrix.data)[[2]])),
192
- y = 1:max(c(dim(matrix.data)[[1]], dim(matrix.data)[[2]])),
193
- z = as.matrix(matrix.data),
194
- col = rev(heat.colors(#{num_colors})),
195
- zlim = #{forced_square ? "c(sort(filtered.values)[2], max(filtered.values))" : "c(min(filtered.values), max(filtered.values))"},
196
- xlab = "#{x_label} (1-indexed)",
197
- ylab = "#{y_label} (1-indexed)"
198
- )
199
- title(#{expressionify(title || "Matrix Heatmap")})
200
- STR
201
- end
202
183
  else
203
184
  puts "Please install the Matrix package for R before using this function."
204
185
  end
@@ -207,15 +188,17 @@ module Wrnap
207
188
 
208
189
  private
209
190
 
210
- def generate_graph(window_title = "ViennaRNA Graph in R", &block)
211
- r, filename = block.binding.eval("[r, filename]")
191
+ def generate_graph(r, width, height, &block)
192
+ filename = block.binding.eval("filename")
212
193
 
213
- r.eval("%s('%s', 6, 6)" % [
194
+ r.eval("%s('%s', %d, %d)" % [
214
195
  writing_file?(filename) ? filetype(filename) : "quartz",
215
196
  writing_file?(filename) ? filename : "Graph",
197
+ width,
198
+ height
216
199
  ])
217
200
 
218
- r.eval(yield)
201
+ yield r
219
202
 
220
203
  r.eval("dev.off()") if writing_file?(filename)
221
204
  end
@@ -10,7 +10,7 @@ module Wrnap
10
10
  include Wrnap::Global::Yaml
11
11
 
12
12
  class_attribute :executable_name
13
- self.executable_name = ->(context) { "RNA#{context.class.name.split('::').last.underscore}" }
13
+ self.executable_name = ->(context) { "RNA#{context.name.split('::').last.underscore}" }
14
14
 
15
15
  class_attribute :call_with
16
16
  self.call_with = [:seq]
@@ -15,7 +15,7 @@ module Wrnap
15
15
  self.quote_flag_params = %w|sequenceDBN structureDBN title|
16
16
 
17
17
  def run_command(flags)
18
- "java fr.orsay.lri.varna.applications.VARNAcmd %s" % stringify_flags(flags)
18
+ "java -Djava.awt.headless=true fr.orsay.lri.varna.applications.VARNAcmd %s" % stringify_flags(flags)
19
19
  end
20
20
  end
21
21
  end
@@ -5,7 +5,7 @@ module Wrnap
5
5
  E: "/usr/local/bin/rna_turner2004.par"
6
6
  }
7
7
 
8
- self.executable_name = ->(context) { context.class.name.demodulize.gsub(/^([A-Z].*)bor$/) { |match| $1.upcase + "bor" } }
8
+ self.executable_name = ->(context) { context.name.demodulize.gsub(/^([A-Z].*)bor$/) { |match| $1.upcase + "bor" } }
9
9
 
10
10
  def run_command(flags)
11
11
  file = Tempfile.new("rna")
data/lib/wrnap/rna/box.rb CHANGED
@@ -9,13 +9,19 @@ module Wrnap
9
9
 
10
10
  class << self
11
11
  def load_all(pattern = "*.fa", &block)
12
- new(Dir[File.directory?(pattern) ? pattern + "/*.fa" : pattern].map { |file| RNA.from_fasta(file, &block) })
12
+ new(Dir[File.directory?(pattern) ? pattern + "/*.fa" : pattern].inject([]) { |array, file| array + RNA.from_fasta(file, &block).rnas })
13
13
  end
14
14
  end
15
15
 
16
16
  def initialize(rnas)
17
17
  @rnas = rnas.kind_of?(Array) ? rnas : [rnas]
18
18
  end
19
+
20
+ def write_fa!(filename)
21
+ filename.tap do |filename|
22
+ File.open(filename, ?w) { |file| file.write(rnas.map(&:formatted_string).join(?\n) + ?\n) }
23
+ end
24
+ end
19
25
 
20
26
  def pp
21
27
  rnas.each(&:pp) and nil
@@ -25,15 +31,27 @@ module Wrnap
25
31
  self.class.new(rnas + (arrayish.is_a?(Box) ? arrayish.rnas : arrayish))
26
32
  end
27
33
 
28
- def_delegators :@rnas, *%i|size length [] []= <<|
34
+ def_delegators :@rnas, *%i|size length first last [] []= <<|
29
35
 
30
36
  def each(&block)
37
+ return enum_for(:each) unless block_given?
31
38
  rnas.each(&block)
32
39
  end
40
+
41
+ def run_in_parallel(method, *args)
42
+ Wrnap.debug, debug_status = false, Wrnap.debug
43
+ Parallel.map(self, progress: "Calling %s on %d RNAs" % [method, rnas.size]) { |rna| rna.send(method, *args) }.tap { Wrnap.debug = debug_status }
44
+ end
33
45
 
34
46
  def kind_of?(klass)
35
47
  klass == Array ? true : super
36
48
  end
49
+
50
+ def method_missing(name, *args, &block)
51
+ if (name_str = "#{name}") =~ /^run_\w+$/
52
+ run_in_parallel(name_str, *args)
53
+ else super end
54
+ end
37
55
 
38
56
  def inspect
39
57
  "#<Wrnap::Rna::Box with %d RNAs>" % rnas.size
@@ -9,6 +9,10 @@ module Wrnap
9
9
  def constraint_mask
10
10
  md[:constraint_mask]
11
11
  end
12
+
13
+ def show_constraints(&block)
14
+ ConstraintBox.new(metadata.__rna__).tap { |box| box.instance_eval(&block) }.to_s
15
+ end
12
16
 
13
17
  def build_constraints(&block)
14
18
  meta_rna do |metadata|
@@ -23,6 +27,10 @@ module Wrnap
23
27
  def initialize(rna)
24
28
  @rna, @constraints = rna, []
25
29
  end
30
+
31
+ def n
32
+ rna.len - 1
33
+ end
26
34
 
27
35
  def between(i, j)
28
36
  Loop.new(i, j)
@@ -32,10 +40,25 @@ module Wrnap
32
40
  between(i + 1, j - 1)
33
41
  end
34
42
 
43
+ def start_to(i)
44
+ between(0, i)
45
+ end
46
+
47
+ def end_to(i)
48
+ between(i, n)
49
+ end
50
+
51
+ def freeze(mask_object)
52
+ force mask_object
53
+ prohibit mask_object.unpaired_regions
54
+ end
55
+
35
56
  def mask!(mask_object, *args)
36
57
  case mask_object
37
- when Helix then mask_helix!(mask_object, *args)
38
- when Loop then mask_loop!(mask_object, symbol: args[0][:symbol])
58
+ when TreeStem then mask_object.preorder_traversal { |node| mask!(node.content, *args) }
59
+ when Array then mask_object.map { |node| mask!(node, *args) }
60
+ when Helix then mask_helix!(mask_object, *args)
61
+ when Loop then mask_loop!(mask_object, symbol: args[0][:symbol])
39
62
  end
40
63
  end
41
64
 
@@ -1,14 +1,16 @@
1
1
  module Wrnap
2
2
  class Rna
3
3
  class Context < Rna
4
+ extend Forwardable
5
+
4
6
  attr_reader :accession, :from, :to, :coord_options
5
7
 
6
8
  class << self
7
9
  def init_from_entrez(accession, from, to, options = {}, &block)
8
10
  new(
9
11
  accession: accession,
10
- from: from,
11
- to: to,
12
+ from: from.to_i,
13
+ to: to.to_i,
12
14
  options: options,
13
15
  &block
14
16
  )
@@ -18,8 +20,8 @@ module Wrnap
18
20
  new(
19
21
  sequence: sequence,
20
22
  accession: accession,
21
- from: from,
22
- to: to,
23
+ from: from.to_i,
24
+ to: to.to_i,
23
25
  options: options,
24
26
  &block
25
27
  )
@@ -102,6 +104,8 @@ module Wrnap
102
104
  end
103
105
 
104
106
  alias :seq :sequence
107
+
108
+ def_delegator :@raw_sequence, :length, :len
105
109
 
106
110
  def extend!(coord_options = {})
107
111
  self.class.init_from_entrez(accession, from, to, coords: coord_options)
@@ -126,14 +130,22 @@ module Wrnap
126
130
  range
127
131
  end
128
132
  end
129
-
133
+
130
134
  def identifier
131
- "%s %d %s %d" % [accession, from, plus_strand? ? ?+ : ?-, to]
135
+ "%s %d %s %d" % [accession, seq_from, plus_strand? ? ?+ : ?-, seq_to]
136
+ end
137
+
138
+ def _id
139
+ identifier.gsub(/[^A-Z0-9]/, ?_).gsub(/__+/, ?_)
132
140
  end
133
141
 
134
142
  def inspect
135
- super.gsub(/((\w(::)?)+)>$/) { |_| "%s %s>" % [identifier, $1] }
143
+ if super.match(/Wrnap::(\w+(::)?)+>$/)
144
+ super.sub(/([\w:]+)>$/) { |_| "%s %s>" % [identifier, $1] }
145
+ else
146
+ super
147
+ end
136
148
  end
137
149
  end
138
150
  end
139
- end
151
+ end
@@ -39,8 +39,53 @@ module Wrnap
39
39
  end
40
40
 
41
41
  module InstanceMethods
42
+ def hamming_distance(other_rna)
43
+ raise "The two sequences are not the same length" unless len == other_rna.len
44
+
45
+ [seq, other_rna.seq].map(&:each_char).map(&:to_a).transpose.select { |array| array.uniq.size > 1 }.count
46
+ end
47
+
48
+ def local_structural_shuffle(dishuffle: false)
49
+ sequence = " " * len
50
+ loops, helices = loops_and_helices
51
+
52
+ loops.each { |loop| sequence[loop.i..loop.j] = Shuffle.new(loop.in(seq)).send(dishuffle ? :dishuffle : :shuffle) }
53
+ helices.each do |helix|
54
+ left_stem, right_stem = if dishuffle
55
+ Shuffle.new(helix.in(seq)).dishuffle
56
+ else
57
+ Shuffle.new(helix.in(seq)).shuffle.map { |array| rand(2).zero? ? array : array.reverse }
58
+ end.transpose.map(&:join)
59
+
60
+ sequence[helix.i..helix.k] = left_stem
61
+ sequence[helix.l..helix.j] = right_stem.reverse
62
+ end
63
+
64
+ Rna.init_from_string(sequence, str)
65
+ end
66
+
67
+ def local_structural_dishuffle
68
+ local_structural_shuffle(dishuffle: true)
69
+ end
70
+
71
+ def global_structural_shuffle
72
+ sequence = " " * len
73
+ loops, helices = loops_and_helices
74
+ shuffled_loops = Shuffle.new(loops.map { |loop| loop.in(seq) }.join.split(//)).shuffle
75
+ shuffled_helices = helices.map { |helix| helix.in(seq) }.inject(&:+).shuffle.map { |array| rand(2).zero? ? array : array.reverse }
76
+
77
+ loops.each { |loop| sequence[loop.i..loop.j] = shuffled_loops.shift(loop.length).join }
78
+ helices.each do |helix|
79
+ left_stem, right_stem = shuffled_helices.shift(helix.length).transpose.map(&:join)
80
+ sequence[helix.i..helix.k] = left_stem
81
+ sequence[helix.l..helix.j] = right_stem.reverse
82
+ end
83
+
84
+ Rna.init_from_string(sequence, str)
85
+ end
86
+
42
87
  def dishuffle
43
- self.class.shuffle(sequence, 2)
88
+ Rna.init_from_string(self.class.shuffle(sequence, 2))
44
89
  end
45
90
 
46
91
  def gc_content
@@ -56,6 +101,33 @@ module Wrnap
56
101
  def max_bp_distance(structure)
57
102
  base_pairs(structure).count + ((structure.length - 3) / 2.0).floor
58
103
  end
104
+
105
+ def loops_and_helices(structure)
106
+ [loop_regions(structure), collapsed_helices(structure, lonely_bp: true)]
107
+ end
108
+
109
+ def loop_regions(structure)
110
+ [structure.split(//), (0...structure.length).to_a].transpose.select { |char, _| char == ?. }.inject([]) do |array, (_, index)|
111
+ array.tap do
112
+ matching_loop = array.map(&:last).each_with_index.find { |end_of_loop, _| end_of_loop + 1 == index }
113
+ matching_loop ? array[matching_loop[-1]][-1] += 1 : array << [index, index]
114
+ end
115
+ end.map { |loop_indices| Loop.new(*loop_indices) }
116
+ end
117
+
118
+ def helices(structure)
119
+ unless (array = base_pairs(structure).sort_by(&:first).map(&:to_a)).empty?
120
+ array[1..-1].inject([[array.first]]) do |bins, (i, j)|
121
+ bins.tap { bins[-1][-1] == [i - 1, j + 1] ? bins[-1] << [i, j] : bins << [[i, j]] }
122
+ end
123
+ else
124
+ []
125
+ end
126
+ end
127
+
128
+ def collapsed_helices(structure, lonely_bp: false)
129
+ helices(structure).map { |((i, j), *rest)| Helix.new(i, j, rest.length + 1) }.select { |helix| lonely_bp ? helix : helix.length > 1 }
130
+ end
59
131
 
60
132
  def base_pairs(structure)
61
133
  get_pairings(structure).each_with_index.inject(Set.new) do |set, (j, i)|
@@ -1,23 +1,5 @@
1
1
  module Wrnap
2
- class Rna
3
- module Motifs
4
- def helices
5
- array = base_pairs.sort_by(&:first).map(&:to_a)
6
-
7
- unless array.empty?
8
- array[1..-1].inject([[array.first]]) do |bins, (i, j)|
9
- bins.tap { bins[-1][-1] == [i - 1, j + 1] ? bins[-1] << [i, j] : bins << [[i, j]] }
10
- end
11
- else
12
- []
13
- end
14
- end
15
-
16
- def collapsed_helices(lonely_bp: false)
17
- helices.map { |((i, j), *rest)| Helix.new(i, j, rest.length + 1) }.select { |helix| lonely_bp ? helix : helix.length > 1 }
18
- end
19
- end
20
-
2
+ class Rna
21
3
  class Helix
22
4
  attr_reader :i, :j, :length
23
5
 
@@ -28,9 +10,26 @@ module Wrnap
28
10
  def k; i + length - 1; end
29
11
  def l; j - length + 1; end
30
12
 
13
+ def in(sequence)
14
+ (0...length).map do |stem_position|
15
+ [sequence[i + stem_position], sequence[j - stem_position]]
16
+ end
17
+ end
18
+
31
19
  def to_loops
32
20
  [Loop.new(i, k), Loop.new(l, j)]
33
21
  end
22
+
23
+ def apply!(structure)
24
+ structure.tap do
25
+ structure[i, length] = ?( * length
26
+ structure[l, length] = ?) * length
27
+ end
28
+ end
29
+
30
+ def merge!(helix)
31
+ @length = helix.k - i + 1
32
+ end
34
33
 
35
34
  def reindex!(rna)
36
35
  tap do
@@ -45,7 +44,7 @@ module Wrnap
45
44
  end
46
45
 
47
46
  def name
48
- "(%d..%d, %d..%d)" % [i, k, l, j]
47
+ "(%d..%d, %d..%d [%d])" % [i, k, l, j, length]
49
48
  end
50
49
 
51
50
  def inspect
@@ -60,8 +59,16 @@ module Wrnap
60
59
  @i, @j = i, j
61
60
  end
62
61
 
62
+ def in(sequence)
63
+ sequence[i..j]
64
+ end
65
+
66
+ def length
67
+ j - i + 1
68
+ end
69
+
63
70
  def name
64
- "(%d, %d)" % [i, j]
71
+ "(%d, %d [%d])" % [i, j, length]
65
72
  end
66
73
 
67
74
  def inspect
@@ -14,19 +14,84 @@ module Wrnap
14
14
  extend Forwardable
15
15
  include Enumerable
16
16
 
17
- STEM_NOTATION_REGEX = /^p((\d+_)*(\d+))(_?([ijkl]))?$/
17
+ STEM_NOTATION_REGEX = /^[pt]_?(\d+_)*\d+(_?[ijkl])?$/
18
18
 
19
- def_delegators :@content, :i, :j
19
+ def_delegators :@content, :i, :j, :k, :l
20
20
  def_delegator :@content, :length, :stem_length
21
+
22
+ def unpaired_regions
23
+ Wrnap.debugger { "Collecting unpaired regions for %s" % [root.content.name] }
24
+
25
+ postorder_traversal.inject([]) do |array, node|
26
+ array.tap do
27
+ if node.is_leaf?
28
+ array << Loop.new(node.k + 1, node.l - 1)
29
+ end
30
+
31
+ if node != self
32
+ if node.is_only_child?
33
+ Wrnap.debugger { "Interior node: %s, parent: %s" % [node.content.inspect, node.parent.content.inspect] }
34
+
35
+ if node.i - node.parent.k > 0
36
+ Wrnap.debugger { "Left bulge." }
37
+ array << Loop.new(node.parent.k + 1, node.i - 1)
38
+ end
39
+
40
+ if node.parent.l - node.j > 0
41
+ Wrnap.debugger { "Right bulge." }
42
+ array << Loop.new(node.j + 1, node.parent.l - 1)
43
+ end
44
+ else
45
+ node_index = node.parent.children.each_with_index.find { |child, _| child == node }.last
46
+
47
+ if node.is_last_sibling?
48
+ Wrnap.debugger { "Leaf node, last child: %s" % node.content.inspect }
49
+ array << Loop.new(node.j + 1, node.parent.l - 1)
50
+ else
51
+ if node.is_first_sibling?
52
+ Wrnap.debugger { "Leaf node, first child: %s" % node.content.inspect }
53
+ array << Loop.new(node.parent.k + 1, node.i - 1)
54
+ end
55
+
56
+ Wrnap.debugger { "Connecting node, middle child: %s" % node.content.inspect }
57
+ alexa = node.siblings[node_index]
58
+ array << Loop.new(node.j + 1, alexa.i - 1)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def detached_copy
67
+ self.class.new(@name, @content ? @content.clone : nil)
68
+ end
69
+
70
+ def preorder_traversal(&block)
71
+ return enum_for(:preorder_traversal) unless block_given?
72
+ yield self
73
+ children.map { |child| child.preorder_traversal(&block) }
74
+ end
75
+
76
+ def postorder_traversal(&block)
77
+ return enum_for(:postorder_traversal) unless block_given?
78
+ children.each { |child| child.postorder_traversal(&block) }
79
+ yield self
80
+ end
21
81
 
22
82
  def method_missing(name, *args, &block)
23
- if name.to_s =~ STEM_NOTATION_REGEX
24
- if $2 && child = children[$2.to_i - 1]
25
- child.send("p%s" % name.to_s.gsub(/^p\d+_/, ""))
26
- elsif child = children[$1.to_i - 1]
27
- $5 ? child.content.send($5) : child.content
28
- else
29
- nil
83
+ if (method_name = name.to_s) =~ STEM_NOTATION_REGEX
84
+ call_type = method_name[0]
85
+ indices = method_name.gsub(/\D+/, ?_).split(?_).reject(&:empty?).map(&:to_i)
86
+ helix_index = method_name.match(/([ijkl])$/) ? $1 : ""
87
+
88
+ if indices.size > 1 && child = children[indices[0] - 1]
89
+ child.send(call_type + indices[1..-1].join(?_) + helix_index)
90
+ elsif child = children[indices[0] - 1]
91
+ case call_type
92
+ when ?p then helix_index.empty? ? child.content : child.send(helix_index)
93
+ when ?t then child
94
+ end
30
95
  end
31
96
  else super end
32
97
  end
@@ -85,14 +150,35 @@ module Wrnap
85
150
  def coalesce!
86
151
  tap { merge_interior_loops! }
87
152
  end
153
+
154
+ def fuse
155
+ self.class.new(rna, root.dup).tap { |tree| tree.extend_interior_loops! }
156
+ end
88
157
 
158
+ def fuse!
159
+ tap { extend_interior_loops! }
160
+ end
161
+
162
+ def extend_interior_loops!
163
+ handle_interior_loops! do |node, child|
164
+ node.content.merge!(child.content)
165
+ child.remove_from_parent!
166
+ child.children.each { |grandchild| node.add(grandchild) }
167
+ end
168
+ end
169
+
89
170
  def merge_interior_loops!
171
+ handle_interior_loops! do |node, child|
172
+ node.parent.add(child)
173
+ node.remove_from_parent!
174
+ end
175
+ end
176
+
177
+ def handle_interior_loops!(&block)
90
178
  root.tap do
91
- self.class.postorder_traversal(root) do |node|
92
- if node.children.count == 1 && !node.is_root?
93
- child = node.children.first
94
- node.parent.add(child)
95
- node.remove_from_parent!
179
+ root.postorder_traversal do |node|
180
+ if node.children.count == 1 && !node.is_root?
181
+ yield(node, node.children.first)
96
182
  end
97
183
  end
98
184
 
@@ -119,18 +205,6 @@ module Wrnap
119
205
  root.send(name, *args, &block)
120
206
  else super end
121
207
  end
122
-
123
- class << self
124
- def preorder_traversal(node, &block)
125
- node.children.map { |child| preorder_traversal(child, &block) }
126
- yield node
127
- end
128
-
129
- def postorder_traversal(node, &block)
130
- node.children.map { |child| postorder_traversal(child, &block) }
131
- yield node
132
- end
133
- end
134
208
  end
135
209
  end
136
210
  end
data/lib/wrnap/rna.rb CHANGED
@@ -5,7 +5,6 @@ module Wrnap
5
5
  include Wrnap::Rna::Extensions
6
6
  include Wrnap::Rna::Wrnapper
7
7
  include Wrnap::Rna::Metadata
8
- include Wrnap::Rna::Motifs
9
8
  include Wrnap::Rna::TreeFunctions
10
9
  include Wrnap::Rna::Constraints
11
10
 
@@ -44,12 +43,19 @@ module Wrnap
44
43
  comment = File.basename(string, string.include?(?.) ? ".%s" % string.split(?.)[-1] : "")
45
44
  string = File.read(string).chomp
46
45
  end
47
-
48
- init_from_string(*string.split(/\n/).reject { |line| line.start_with?(">") }[0, 3], &block).tap do |rna|
49
- if (line = string.split(/\n/).first).start_with?(">") && !(file_comment = line.gsub(/^>\s*/, "")).empty?
50
- rna.comment = file_comment
51
- elsif comment
52
- rna.comment = comment
46
+
47
+ if string.count(?>) > 1
48
+ string.split(/>/).reject(&:empty?).map do |rna_string|
49
+ rna_data = rna_string.split(?\n)
50
+ init_from_string(*rna_data[1..-1]).copy_name_from(rna_data[0])
51
+ end.wrnap
52
+ else
53
+ init_from_string(*string.split(/\n/).reject { |line| line.start_with?(">") }[0, 3], &block).tap do |rna|
54
+ if (line = string.split(/\n/).first).start_with?(">") && !(file_comment = line.gsub(/^>\s*/, "")).empty?
55
+ rna.comment = file_comment
56
+ elsif comment
57
+ rna.comment = comment
58
+ end
53
59
  end
54
60
  end
55
61
  end
@@ -62,8 +68,8 @@ module Wrnap
62
68
  # This happens when you call a Wrnap library function with the output of something like Wrnap::Fold.run(...).mfe
63
69
  new(
64
70
  sequence: rna.sequence,
65
- strucutre: rna.structure,
66
- second_strucutre: rna.second_structure,
71
+ structure: rna.structure,
72
+ second_structure: rna.second_structure,
67
73
  comment: rna.comment,
68
74
  &block
69
75
  )
@@ -111,8 +117,8 @@ module Wrnap
111
117
 
112
118
  def_delegator :@sequence, :length, :len
113
119
 
114
- def copy_name_from(rna)
115
- tap { @comment = rna.name }
120
+ def copy_name_from(nameish)
121
+ tap { @comment = nameish.is_a?(String) ? nameish : nameish.name }
116
122
  end
117
123
 
118
124
  def empty_structure
@@ -142,15 +148,19 @@ module Wrnap
142
148
  end
143
149
 
144
150
  alias :two_str :two_structures
151
+
152
+ def formatted_string
153
+ [
154
+ (">%s" % name if name),
155
+ ("%s" % seq if seq),
156
+ ("%s" % str_1 if str_1),
157
+ ("%s" % str_2 if str_2)
158
+ ].compact.join(?\n)
159
+ end
145
160
 
146
161
  def write_fa!(filename)
147
162
  filename.tap do |filename|
148
- File.open(filename, ?w) do |file|
149
- file.write("> %s\n" % name) if name
150
- file.write("%s\n" % seq) if seq
151
- file.write("%s\n" % str_1) if str_1
152
- file.write("%s\n" % str_2) if str_2
153
- end
163
+ File.open(filename, ?w) { |file| file.write(formatted_string) }
154
164
  end
155
165
  end
156
166
 
@@ -177,18 +187,14 @@ module Wrnap
177
187
  end
178
188
 
179
189
  def pp
180
- puts("> %s" % name) if name
181
- puts("%s" % seq) if seq
182
- puts("%s" % str_1) if str_1
183
- puts("%s" % str_2) if str_2
184
- puts("%s" % meta.inspect) if meta
190
+ puts(formatted_string)
185
191
  end
186
-
192
+
187
193
  def inspect
188
194
  "#<RNA: %s>" % [
189
- ("#{seq[0, 20] + (len > 20 ? '... [%d]' % len : '')}" if seq && !seq.empty?),
190
- ("#{str_1[0, 20] + (str_1.length > 20 ? ' [%d]' % len : '')}" if str_1 && !str_1.empty?),
191
- ("#{str_2[0, 20] + (str_2.length > 20 ? ' [%d]' % len : '')}" if str_2 && !str_1.empty?),
195
+ ("#{seq[0, 20] + (len > 20 ? '... [%d]' % len : '')}" if seq && !seq.empty?),
196
+ ("#{str_1[0, 20] + (str_1.length > 20 ? ' [%d]' % str_1.length : '')}" if str_1 && !str_1.empty?),
197
+ ("#{str_2[0, 20] + (str_2.length > 20 ? ' [%d]' % str_2.length : '')}" if str_2 && !str_1.empty?),
192
198
  (md.inspect unless md.nil? || md.empty?),
193
199
  (name ? name : "#{self.class.name}")
194
200
  ].compact.join(", ")
data/lib/wrnap/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Wrnap
2
- VERSION = "1.0.1"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/wrnap.rb CHANGED
@@ -1,16 +1,17 @@
1
- require "yaml"
1
+ require "active_support/core_ext/class"
2
+ require "active_support/inflector"
2
3
  require "benchmark"
3
- require "set"
4
- require "tree"
5
- require "shuffle"
6
- require "tempfile"
7
4
  require "bigdecimal"
8
- require "rroc"
9
5
  require "bio"
10
6
  require "bio-stockholm"
11
7
  require "entrez"
12
- require "active_support/inflector"
13
- require "active_support/core_ext/class"
8
+ require "parallel"
9
+ require "rroc"
10
+ require "set"
11
+ require "shuffle"
12
+ require "tempfile"
13
+ require "tree"
14
+ require "yaml"
14
15
 
15
16
  unless %x[which R].empty?
16
17
  require "rinruby"
data/wrnap.gemspec CHANGED
@@ -23,12 +23,14 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "bundler", "~> 1.6"
24
24
  spec.add_development_dependency "rake"
25
25
 
26
- spec.add_runtime_dependency "activesupport", "~> 4.0"
27
- spec.add_runtime_dependency "shuffle", "~> 0.1"
28
- spec.add_runtime_dependency "rinruby", "~> 2.0"
29
- spec.add_runtime_dependency "rroc", "~> 0.1"
30
- spec.add_runtime_dependency "bio", "~> 1.4"
31
- spec.add_runtime_dependency "bio-stockholm", "~> 0.0.1"
32
- spec.add_runtime_dependency "entrez", "~> 0.5"
33
- spec.add_runtime_dependency "rubytree", "~> 0.9"
26
+ spec.add_runtime_dependency "activesupport", "~> 4.0"
27
+ spec.add_runtime_dependency "bio", "~> 1.4"
28
+ spec.add_runtime_dependency "bio-stockholm", "~> 0.0.1"
29
+ spec.add_runtime_dependency "entrez", "~> 0.5"
30
+ spec.add_runtime_dependency "parallel", "~> 1.3", ">= 1.3.2"
31
+ spec.add_runtime_dependency "rinruby", "~> 2.0"
32
+ spec.add_runtime_dependency "rroc", "~> 0.1"
33
+ spec.add_runtime_dependency "ruby-progressbar", "~> 1.5", ">= 1.5.1"
34
+ spec.add_runtime_dependency "rubytree", "~> 0.9"
35
+ spec.add_runtime_dependency "shuffle", "~> 1.0", ">= 1.0.1"
34
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wrnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Senter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-31 00:00:00.000000000 Z
11
+ date: 2014-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,89 +53,115 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '4.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: shuffle
56
+ name: bio
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.1'
61
+ version: '1.4'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.1'
68
+ version: '1.4'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rinruby
70
+ name: bio-stockholm
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '2.0'
75
+ version: 0.0.1
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '2.0'
82
+ version: 0.0.1
83
83
  - !ruby/object:Gem::Dependency
84
- name: rroc
84
+ name: entrez
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.1'
89
+ version: '0.5'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.1'
96
+ version: '0.5'
97
97
  - !ruby/object:Gem::Dependency
98
- name: bio
98
+ name: parallel
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '1.4'
103
+ version: '1.3'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 1.3.2
104
107
  type: :runtime
105
108
  prerelease: false
106
109
  version_requirements: !ruby/object:Gem::Requirement
107
110
  requirements:
108
111
  - - "~>"
109
112
  - !ruby/object:Gem::Version
110
- version: '1.4'
113
+ version: '1.3'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 1.3.2
111
117
  - !ruby/object:Gem::Dependency
112
- name: bio-stockholm
118
+ name: rinruby
113
119
  requirement: !ruby/object:Gem::Requirement
114
120
  requirements:
115
121
  - - "~>"
116
122
  - !ruby/object:Gem::Version
117
- version: 0.0.1
123
+ version: '2.0'
118
124
  type: :runtime
119
125
  prerelease: false
120
126
  version_requirements: !ruby/object:Gem::Requirement
121
127
  requirements:
122
128
  - - "~>"
123
129
  - !ruby/object:Gem::Version
124
- version: 0.0.1
130
+ version: '2.0'
125
131
  - !ruby/object:Gem::Dependency
126
- name: entrez
132
+ name: rroc
127
133
  requirement: !ruby/object:Gem::Requirement
128
134
  requirements:
129
135
  - - "~>"
130
136
  - !ruby/object:Gem::Version
131
- version: '0.5'
137
+ version: '0.1'
132
138
  type: :runtime
133
139
  prerelease: false
134
140
  version_requirements: !ruby/object:Gem::Requirement
135
141
  requirements:
136
142
  - - "~>"
137
143
  - !ruby/object:Gem::Version
138
- version: '0.5'
144
+ version: '0.1'
145
+ - !ruby/object:Gem::Dependency
146
+ name: ruby-progressbar
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.5'
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 1.5.1
155
+ type: :runtime
156
+ prerelease: false
157
+ version_requirements: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - "~>"
160
+ - !ruby/object:Gem::Version
161
+ version: '1.5'
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: 1.5.1
139
165
  - !ruby/object:Gem::Dependency
140
166
  name: rubytree
141
167
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +176,26 @@ dependencies:
150
176
  - - "~>"
151
177
  - !ruby/object:Gem::Version
152
178
  version: '0.9'
179
+ - !ruby/object:Gem::Dependency
180
+ name: shuffle
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: '1.0'
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: 1.0.1
189
+ type: :runtime
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - "~>"
194
+ - !ruby/object:Gem::Version
195
+ version: '1.0'
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: 1.0.1
153
199
  description: ''
154
200
  email:
155
201
  - evansenter@gmail.com
@@ -226,8 +272,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
226
272
  version: '0'
227
273
  requirements: []
228
274
  rubyforge_project:
229
- rubygems_version: 2.2.2
275
+ rubygems_version: 2.4.2
230
276
  signing_key:
231
277
  specification_version: 4
232
278
  summary: A comprehensive wrapper (wRNApper) for various RNA CLI programs.
233
279
  test_files: []
280
+ has_rdoc: