sequence_logo 1.0.6 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/lib/sequence_logo/alignment.rb +70 -0
- data/lib/sequence_logo/assets/nucl_simpa_bw/a.png +0 -0
- data/lib/sequence_logo/assets/nucl_simpa_bw/c.png +0 -0
- data/lib/sequence_logo/assets/nucl_simpa_bw/g.png +0 -0
- data/lib/sequence_logo/assets/nucl_simpa_bw/t.png +0 -0
- data/lib/sequence_logo/canvas_factory.rb +96 -0
- data/lib/sequence_logo/canvases.rb +4 -0
- data/lib/sequence_logo/canvases/gluing_canvas.rb +35 -0
- data/lib/sequence_logo/canvases/horizontal_gluing_canvas.rb +20 -0
- data/lib/sequence_logo/canvases/logo_canvas.rb +40 -0
- data/lib/sequence_logo/canvases/vertical_gluing_canvas.rb +18 -0
- data/lib/sequence_logo/cli.rb +3 -1
- data/lib/sequence_logo/data_models.rb +4 -0
- data/lib/sequence_logo/data_models/ppm_logo.rb +63 -0
- data/lib/sequence_logo/data_models/predefined_logo.rb +29 -0
- data/lib/sequence_logo/data_models/sequence.rb +40 -0
- data/lib/sequence_logo/data_models/sequence_with_snp.rb +45 -0
- data/lib/sequence_logo/exec/glue_logos.rb +68 -47
- data/lib/sequence_logo/exec/sequence_logo.rb +81 -17
- data/lib/sequence_logo/magick_support.rb +14 -0
- data/lib/sequence_logo/pmflogo_lib.rb +4 -113
- data/lib/sequence_logo/support.rb +5 -0
- data/lib/sequence_logo/version.rb +1 -1
- data/lib/sequence_logo/ytilib.rb +1 -1
- data/lib/sequence_logo/ytilib/pm.rb +14 -2
- data/lib/sequence_logo/ytilib/ppm_support.rb +10 -25
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ed1b2f635b68cfa6d69d47f1e122c81f917e286
|
4
|
+
data.tar.gz: 493c1d3b807804a5f9ee670df8d7fbc5f6fe883f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6f56777b12c9dd26c0f3523f3762246baa42a070b045126fabb1b3250b9bba5baa4ff8685a80d06c2b8fabdd095a0c5512a3c0c7096016dd65f1983b24f12e8
|
7
|
+
data.tar.gz: d8bed3b3cab1287681a440b020166f8a5012f02ffb61bfb87ddb7437d23b07298c85cd68fc8a0713020ec8470362d7ea0ae4ad2738140553dfc595f7e5d00752
|
data/README.md
CHANGED
@@ -38,16 +38,17 @@ SequenceLogo consists of two tools:
|
|
38
38
|
|
39
39
|
* Tool **glue_logos** generates a single image of aligned motifs.
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
```glue_logos <output file> <file with alignment infos>```
|
42
|
+
|
43
43
|
or
|
44
44
|
|
45
|
-
|
45
|
+
<alignment infos> | glue_logos <output file>
|
46
|
+
|
47
|
+
Input data comes either from file with alignments or from stdin. *glue_logos* is designated to work fine with macroape *align_motifs* tool and has input format the same as output format of *align_motifs* tool:
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
pcm_file_3 shift_3 orientation_3
|
49
|
+
pcm_file_1 shift_1 orientation_1 [motif_name_1]
|
50
|
+
pcm_file_2 shift_2 orientation_2 [motif_name_2]
|
51
|
+
pcm_file_3 shift_3 orientation_3 [motif_name_3]
|
51
52
|
|
52
53
|
So it's simple to run
|
53
54
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative 'canvases'
|
2
|
+
|
3
|
+
module SequenceLogo
|
4
|
+
class Alignment
|
5
|
+
# object to be aligned should respond to #name, #render, #revcomp
|
6
|
+
class Item
|
7
|
+
attr_reader :object, :shift
|
8
|
+
|
9
|
+
def initialize(object, shift)
|
10
|
+
@object, @shift = object, shift
|
11
|
+
end
|
12
|
+
|
13
|
+
def length
|
14
|
+
@object.length
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@object.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(canvas_factory)
|
22
|
+
object_image = object.render(canvas_factory)
|
23
|
+
shifted_image = canvas_factory.shifted_logo(object_image, shift)
|
24
|
+
canvas_factory.logo_with_name(shifted_image, name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
####################
|
29
|
+
|
30
|
+
def initialize(items = [])
|
31
|
+
@alignable_items = items
|
32
|
+
end
|
33
|
+
|
34
|
+
def +(item)
|
35
|
+
Alignment.new(@alignable_items + [item])
|
36
|
+
end
|
37
|
+
|
38
|
+
def revcomp
|
39
|
+
items_reversed = @alignable_items.map{|item|
|
40
|
+
shift_reversed = rightmost_position - item.shift - item.length
|
41
|
+
Item.new(item.object.revcomp, shift_reversed)
|
42
|
+
}
|
43
|
+
Alignment.new(items_reversed)
|
44
|
+
end
|
45
|
+
|
46
|
+
def render(canvas_factory)
|
47
|
+
canvas = VerticalGluingCanvas.new
|
48
|
+
items_normalized.each do |item|
|
49
|
+
canvas.add_image item.render(canvas_factory)
|
50
|
+
end
|
51
|
+
canvas.background(Magick::HatchFill.new('white', 'white'))
|
52
|
+
canvas.image
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# return list of items shifted altogether such that minimal shift is zero
|
58
|
+
def items_normalized
|
59
|
+
@alignable_items.map{|item| Item.new(item.object, item.shift - leftmost_shift) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def leftmost_shift
|
63
|
+
@alignable_items.map(&:shift).min
|
64
|
+
end
|
65
|
+
|
66
|
+
def rightmost_position
|
67
|
+
@alignable_items.map{|item| item.shift + item.length }.max
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'magick_support'
|
2
|
+
require_relative 'canvases'
|
3
|
+
|
4
|
+
module SequenceLogo
|
5
|
+
class CanvasFactory
|
6
|
+
attr_reader :x_unit, :y_unit, :text_size, :logo_shift
|
7
|
+
attr_reader :letter_images
|
8
|
+
|
9
|
+
def initialize(letter_images, options = {})
|
10
|
+
@letter_images = letter_images # .map{|letter_image| letter_image.dup.resize(x_size, y_size) }
|
11
|
+
@logo_shift = options[:logo_shift] || 300
|
12
|
+
@x_unit = options[:x_unit] || 30
|
13
|
+
@y_unit = options[:y_unit] || 60
|
14
|
+
@text_size = options[:text_size] || 24
|
15
|
+
end
|
16
|
+
|
17
|
+
def text_image(text, img_height = y_unit)
|
18
|
+
text_img = Magick::Image.new(logo_shift, img_height){ self.background_color = 'transparent' }
|
19
|
+
annotation = Magick::Draw.new
|
20
|
+
annotation.pointsize(text_size)
|
21
|
+
annotation.text(10, img_height / 2, text)
|
22
|
+
annotation.draw(text_img)
|
23
|
+
text_img
|
24
|
+
end
|
25
|
+
|
26
|
+
def shifted_logo(image, shift)
|
27
|
+
canvas = HorizontalGluingCanvas.new
|
28
|
+
canvas.add_image Magick::Image.new(shift * x_unit, image.rows){ self.background_color = 'transparent' }
|
29
|
+
canvas.add_image image
|
30
|
+
canvas.image
|
31
|
+
end
|
32
|
+
|
33
|
+
def logo_with_name(image, name)
|
34
|
+
canvas = HorizontalGluingCanvas.new
|
35
|
+
canvas.add_image text_image(name, image.rows)
|
36
|
+
canvas.add_image image
|
37
|
+
canvas.image
|
38
|
+
end
|
39
|
+
|
40
|
+
def logo_canvas
|
41
|
+
LogoCanvas.new(letter_images, x_unit: x_unit, y_unit: y_unit)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Takes an enumerable with relative (0 to 1) heights of letters and draws them scaled appropriately
|
45
|
+
def logo_for_ordered_letters(letters_with_heights)
|
46
|
+
logo_for_ordered_letters_nonscaling(rescale_letters(letters_with_heights))
|
47
|
+
end
|
48
|
+
|
49
|
+
# Takes an enumerable with height=>letter pairs draws a logo position with letters in order of enumeration
|
50
|
+
# It's a basic logo-block.
|
51
|
+
def logo_for_ordered_letters_nonscaling(letters_with_heights)
|
52
|
+
y_pos = 0
|
53
|
+
position_logo = Magick::ImageList.new
|
54
|
+
position_logo.set_minimal_size(x_unit, y_unit)
|
55
|
+
letters_with_heights.each do |height, letter|
|
56
|
+
y_pos += height
|
57
|
+
position_logo.put_image_at(letter_image(letter, x_unit, height), 0, y_unit - y_pos)
|
58
|
+
end
|
59
|
+
position_logo.flatten_images
|
60
|
+
end
|
61
|
+
|
62
|
+
def rescale_letters(letters_with_heights)
|
63
|
+
letters_with_heights
|
64
|
+
.reject{|part_of_height, letter| y_unit * part_of_height <= 1 }
|
65
|
+
.map{|part_of_height, letter| [(y_unit * part_of_height), letter] }
|
66
|
+
end
|
67
|
+
private :logo_for_ordered_letters_nonscaling, :rescale_letters
|
68
|
+
|
69
|
+
def letter_image(letter, x_size = x_unit, y_size = y_unit)
|
70
|
+
case letter
|
71
|
+
when Numeric
|
72
|
+
index = letter
|
73
|
+
else
|
74
|
+
index = letter_index(letter)
|
75
|
+
end
|
76
|
+
letter_images[index].dup.resize(x_size, y_size)
|
77
|
+
end
|
78
|
+
|
79
|
+
def letter_index(letter)
|
80
|
+
{'A' => 0 ,'C' => 1,'G' => 2 ,'T' => 3}[letter.to_s.upcase]
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.letter_images(scheme_dir)
|
84
|
+
if File.exist?(File.join(scheme_dir,'a.png'))
|
85
|
+
extension = 'png'
|
86
|
+
elsif File.exist?(File.join(scheme_dir,'a.gif'))
|
87
|
+
extension = 'gif'
|
88
|
+
else
|
89
|
+
raise "Scheme not exists in folder #{scheme_dir}"
|
90
|
+
end
|
91
|
+
|
92
|
+
letter_files = %w[a c g t].collect{|letter| File.join(scheme_dir, "#{letter}.#{extension}") }
|
93
|
+
Magick::ImageList.new(*letter_files)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../magick_support'
|
2
|
+
|
3
|
+
module SequenceLogo
|
4
|
+
class GluingCanvas
|
5
|
+
attr_reader :i_logo, :size
|
6
|
+
def initialize
|
7
|
+
@i_logo = Magick::ImageList.new
|
8
|
+
@size = 0
|
9
|
+
@rendering_callbacks = []
|
10
|
+
@rendering_callbacks << method(:render_background)
|
11
|
+
end
|
12
|
+
|
13
|
+
def image
|
14
|
+
@rendering_callbacks.each(&:call)
|
15
|
+
@i_logo.flatten_images
|
16
|
+
end
|
17
|
+
|
18
|
+
def background(fill)
|
19
|
+
@background_fill = fill
|
20
|
+
end
|
21
|
+
|
22
|
+
def render_background
|
23
|
+
if @background_fill
|
24
|
+
@i_logo.unshift Magick::Image.new(x_size, y_size, @background_fill)
|
25
|
+
else
|
26
|
+
@i_logo.set_minimal_size(x_size, y_size)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
private :render_background
|
30
|
+
|
31
|
+
def add_image(item)
|
32
|
+
@size += 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'gluing_canvas'
|
2
|
+
|
3
|
+
module SequenceLogo
|
4
|
+
class HorizontalGluingCanvas < GluingCanvas
|
5
|
+
alias_method :length, :size
|
6
|
+
|
7
|
+
def add_image(image)
|
8
|
+
super
|
9
|
+
@i_logo.put_image_at(image, x_size, 0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def x_size
|
13
|
+
@i_logo.to_a.map(&:columns).inject(0, :+)
|
14
|
+
end
|
15
|
+
|
16
|
+
def y_size
|
17
|
+
@i_logo.to_a.map(&:rows).max || 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
require_relative '../magick_support'
|
3
|
+
require_relative 'horizontal_gluing_canvas'
|
4
|
+
|
5
|
+
module SequenceLogo
|
6
|
+
class LogoCanvas < HorizontalGluingCanvas
|
7
|
+
attr_reader :canvas_factory
|
8
|
+
def initialize(canvas_factory)
|
9
|
+
super()
|
10
|
+
@canvas_factory = canvas_factory
|
11
|
+
end
|
12
|
+
|
13
|
+
def draw_threshold_line(threshold_level)
|
14
|
+
# stores threshold levels but doesn't render them because full length of canvas is not known yet,
|
15
|
+
# so instantly rendered line would be too short
|
16
|
+
@rendering_callbacks.push ->{ render_threshold_line(threshold_level) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def render_threshold_line(threshold_level)
|
20
|
+
y_coord = y_size - threshold_level * y_size
|
21
|
+
dr = Magick::Draw.new
|
22
|
+
dr.fill('transparent')
|
23
|
+
|
24
|
+
dr.stroke_width(y_size / 200.0)
|
25
|
+
dr.stroke_dasharray(7,7)
|
26
|
+
|
27
|
+
dr.stroke('silver')
|
28
|
+
dr.line(0, y_coord, x_size, y_coord)
|
29
|
+
dr.draw(@i_logo)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_letter(letter)
|
33
|
+
add_image( canvas_factory.letter_image(letter) )
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_position_ordered(ordered_letter_heights)
|
37
|
+
add_image( canvas_factory.logo_for_ordered_letters(ordered_letter_heights) )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'gluing_canvas'
|
2
|
+
|
3
|
+
module SequenceLogo
|
4
|
+
class VerticalGluingCanvas < GluingCanvas
|
5
|
+
def add_image(image)
|
6
|
+
super
|
7
|
+
@i_logo.put_image_at(image, 0, y_size)
|
8
|
+
end
|
9
|
+
|
10
|
+
def x_size
|
11
|
+
@i_logo.to_a.map(&:columns).max || 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def y_size
|
15
|
+
@i_logo.to_a.map(&:rows).inject(0, :+)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/sequence_logo/cli.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative '../canvases'
|
2
|
+
|
3
|
+
module SequenceLogo
|
4
|
+
# wrapper around PPM to make it possible to configure rendering in a flexible way
|
5
|
+
class PPMLogo
|
6
|
+
attr_reader :ppm, :words_count, :icd_mode, :enable_threshold_lines
|
7
|
+
|
8
|
+
def initialize(ppm, options = {})
|
9
|
+
@ppm = ppm
|
10
|
+
@words_count = options[:words_count]
|
11
|
+
@icd_mode = options[:icd_mode]
|
12
|
+
@enable_threshold_lines = options[:enable_threshold_lines]
|
13
|
+
|
14
|
+
@ppm.words_count = @words_count if @words_count
|
15
|
+
unless ppm.words_count
|
16
|
+
report "words count for PPM is undefined, assuming weblogo mode"
|
17
|
+
@icd_mode = :weblogo
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def length
|
22
|
+
ppm.length
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
ppm.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def revcomp
|
30
|
+
PPMLogo.new(ppm.revcomp, words_count: words_count, icd_mode: icd_mode, enable_threshold_lines: enable_threshold_lines)
|
31
|
+
end
|
32
|
+
|
33
|
+
def logo_matrix
|
34
|
+
ppm.get_logo(icd_mode)
|
35
|
+
end
|
36
|
+
|
37
|
+
def render(canvas_factory)
|
38
|
+
canvas = LogoCanvas.new(canvas_factory)
|
39
|
+
if icd_mode == :discrete
|
40
|
+
canvas.background(Magick::HatchFill.new('white', 'white'))
|
41
|
+
if enable_threshold_lines
|
42
|
+
canvas.draw_threshold_line(ppm.get_line(ppm.icd2of4))
|
43
|
+
canvas.draw_threshold_line(ppm.get_line(ppm.icdThc))
|
44
|
+
canvas.draw_threshold_line(ppm.get_line(ppm.icdTlc))
|
45
|
+
end
|
46
|
+
else
|
47
|
+
canvas.background(Magick::HatchFill.new('white', 'bisque'))
|
48
|
+
end
|
49
|
+
|
50
|
+
logo_matrix.each do |position|
|
51
|
+
canvas.add_position_ordered( position_sorted_by_height(position) )
|
52
|
+
end
|
53
|
+
canvas.image
|
54
|
+
end
|
55
|
+
|
56
|
+
# [3,1,1,2] ==> [[3, 0],[2, 3],[1, 1],[1, 2]] (derived from [[3, 'A'],[2,'T'],[1,'C'],[1,'G']])
|
57
|
+
def position_sorted_by_height(position)
|
58
|
+
# sort by [count, letter_index] allows us to make stable sort by count (it's useful for predictable order of same-height nucleotides)
|
59
|
+
position.each_with_index.sort_by{|count, letter_index| [count, letter_index] }.reverse
|
60
|
+
end
|
61
|
+
private :position_sorted_by_height
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SequenceLogo
|
2
|
+
class PredefinedLogo
|
3
|
+
attr_reader :direct_image, :reverse_image
|
4
|
+
def initialize(options = {})
|
5
|
+
@direct_image = options[:direct_image]
|
6
|
+
@reverse_image = options[:reverse_image]
|
7
|
+
@name = options[:name]
|
8
|
+
@length = options[:length]
|
9
|
+
end
|
10
|
+
|
11
|
+
def length
|
12
|
+
raise 'Length not defined' unless @length
|
13
|
+
@length
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
raise 'Name not defined' unless @name
|
18
|
+
@name
|
19
|
+
end
|
20
|
+
|
21
|
+
def revcomp
|
22
|
+
PredefinedLogo.new(direct_image: @reverse_image, reverse_image: @direct_image, name: @name, length: @length)
|
23
|
+
end
|
24
|
+
|
25
|
+
def render(canvas_factory)
|
26
|
+
@direct_image
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative '../canvases'
|
2
|
+
|
3
|
+
module SequenceLogo
|
4
|
+
class Sequence
|
5
|
+
attr_reader :sequence, :name
|
6
|
+
def initialize(sequence, options = {})
|
7
|
+
raise 'Wrong sequence' unless Sequence.valid_sequence?(sequence)
|
8
|
+
@sequence = sequence
|
9
|
+
@name = options[:name] || sequence
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
sequence.length
|
14
|
+
end
|
15
|
+
|
16
|
+
def revcomp
|
17
|
+
Sequence.new(Sequence.revcomp(sequence), name: name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(canvas_factory)
|
21
|
+
canvas = LogoCanvas.new(canvas_factory)
|
22
|
+
canvas.background(Magick::HatchFill.new('white', 'white'))
|
23
|
+
sequence.each_char do |letter|
|
24
|
+
canvas.add_letter(letter)
|
25
|
+
end
|
26
|
+
canvas.image
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.complement(sequence)
|
30
|
+
sequence.tr('acgtACGT', 'tgcaTGCA')
|
31
|
+
end
|
32
|
+
def self.revcomp(sequence)
|
33
|
+
complement(sequence).reverse
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.valid_sequence?(sequence)
|
37
|
+
sequence.match /\A[acgt]+\z/i
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative 'sequence'
|
2
|
+
require_relative '../canvases'
|
3
|
+
|
4
|
+
module SequenceLogo
|
5
|
+
class SequenceWithSNP
|
6
|
+
attr_reader :left, :allele_variants, :right, :name
|
7
|
+
def initialize(left, allele_variants, right, options = {})
|
8
|
+
raise unless Sequence.valid_sequence?(left)
|
9
|
+
raise unless Sequence.valid_sequence?(right)
|
10
|
+
raise unless allele_variants.all?{|letter| %w[A C G T].include?(letter.upcase) }
|
11
|
+
@left, @allele_variants, @right = left, allele_variants, right
|
12
|
+
@name = options[:name] || (left + '_' + allele_variants.join('-') + '_' + right)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_string(sequence, options = {})
|
16
|
+
left, mid, right = sequence.split(/[\[\]]/)
|
17
|
+
allele_variants = mid.split('/')
|
18
|
+
SequenceWithSNP.new(left, allele_variants, right, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def length
|
22
|
+
left.length + 1 + right.length
|
23
|
+
end
|
24
|
+
|
25
|
+
def revcomp
|
26
|
+
SequenceWithSNP.new(Sequence.revcomp(right),
|
27
|
+
allele_variants.map{|letter| Sequence.complement(letter) },
|
28
|
+
Sequence.revcomp(left))
|
29
|
+
end
|
30
|
+
|
31
|
+
def render(canvas_factory)
|
32
|
+
canvas = LogoCanvas.new(canvas_factory)
|
33
|
+
canvas.background(Magick::HatchFill.new('white', 'white'))
|
34
|
+
left.each_char{|letter| canvas.add_letter(letter) }
|
35
|
+
canvas.add_position_ordered(snp_position_heights)
|
36
|
+
right.each_char{|letter| canvas.add_letter(letter) }
|
37
|
+
canvas.image
|
38
|
+
end
|
39
|
+
|
40
|
+
def snp_position_heights
|
41
|
+
allele_variants.map{|letter| [1.0 / allele_variants.size, letter] }
|
42
|
+
end
|
43
|
+
private :snp_position_heights
|
44
|
+
end
|
45
|
+
end
|
@@ -1,43 +1,64 @@
|
|
1
1
|
require_relative '../../sequence_logo'
|
2
2
|
require 'fileutils'
|
3
|
-
require 'cgi'
|
4
3
|
require 'tempfile'
|
5
4
|
|
6
|
-
def
|
7
|
-
|
8
|
-
logo_files = []
|
9
|
-
rightmost_side = alignment_infos.map do |line|
|
5
|
+
def load_alignment_infos(alignment_lines)
|
6
|
+
alignment_lines.map{|line|
|
10
7
|
filename, shift, orientation, motif_name = line.strip.split("\t")
|
8
|
+
motif_name ||= File.basename(filename, File.extname(filename))
|
11
9
|
shift = shift.to_i
|
12
|
-
|
13
|
-
end.max
|
10
|
+
orientation = orientation.downcase.to_sym
|
14
11
|
|
15
|
-
alignment_infos.each do |line|
|
16
|
-
filename, shift, orientation, motif_name = line.strip.split("\t")
|
17
|
-
motif_name ||= CGI.unescape(File.basename(filename, File.extname(filename)))
|
18
12
|
ppm = get_ppm_from_file(filename)
|
19
|
-
shift = shift.to_i
|
20
|
-
raise 'Unknown orientation' unless %w[direct revcomp].include?(orientation.downcase)
|
21
|
-
if total_orientation == :revcomp
|
22
|
-
orientation = (orientation == 'direct') ? 'revcomp' : 'direct'
|
23
|
-
shift = rightmost_side - shift - ppm.length
|
24
|
-
end
|
25
13
|
checkerr("bad input file: #{filename}") { ppm == nil }
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
14
|
+
ppm.name ||= motif_name
|
15
|
+
|
16
|
+
raise 'Unknown orientation' unless [:direct, :revcomp].include?(orientation)
|
17
|
+
|
18
|
+
ppm_oriented = (orientation == :direct) ? ppm : ppm.revcomp
|
19
|
+
{motif: ppm_oriented, shift: shift}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def make_logo_alignment(aligned_motifs, options)
|
24
|
+
alignment = SequenceLogo::Alignment.new
|
25
|
+
aligned_motifs.map {|motif_infos|
|
26
|
+
ppm_logo = SequenceLogo::PPMLogo.new(motif_infos[:motif],
|
27
|
+
icd_mode: options[:icd_mode],
|
28
|
+
words_count: options[:words_count],
|
29
|
+
enable_threshold_lines: options[:threshold_lines])
|
30
|
+
alignment += SequenceLogo::Alignment::Item.new(ppm_logo, motif_infos[:shift])
|
31
|
+
}
|
32
|
+
alignment
|
33
|
+
end
|
34
|
+
|
35
|
+
def readlines_from_file_or_stdin(argv, options = {})
|
36
|
+
default_options = { source_not_given_msg: 'Specify input data',
|
37
|
+
both_sources_given_msg: 'Specify either file with data or data itself in stdin, not both'}
|
38
|
+
options = default_options.merge(options)
|
39
|
+
raise options[:both_sources_given_msg] if !argv.empty? && !$stdin.tty?
|
40
|
+
if !argv.empty?
|
41
|
+
lines = File.readlines(argv.first)
|
42
|
+
elsif !$stdin.tty?
|
43
|
+
lines = $stdin.readlines
|
44
|
+
else
|
45
|
+
raise ArgumentError, options[:source_not_given_msg]
|
37
46
|
end
|
47
|
+
lines
|
48
|
+
end
|
38
49
|
|
39
|
-
|
40
|
-
|
50
|
+
def direct_output_filename(output_file)
|
51
|
+
extname = File.extname(output_file)
|
52
|
+
basename = File.basename_wo_extname(output_file)
|
53
|
+
dirname = File.dirname(output_file)
|
54
|
+
File.join(dirname, "#{basename}_direct#{extname}")
|
55
|
+
end
|
56
|
+
|
57
|
+
def reverse_output_filename(output_file)
|
58
|
+
extname = File.extname(output_file)
|
59
|
+
basename = File.basename_wo_extname(output_file)
|
60
|
+
dirname = File.dirname(output_file)
|
61
|
+
File.join(dirname, "#{basename}_revcomp#{extname}")
|
41
62
|
end
|
42
63
|
|
43
64
|
begin
|
@@ -57,7 +78,7 @@ begin
|
|
57
78
|
|
58
79
|
argv = ARGV
|
59
80
|
total_orientation = :direct
|
60
|
-
default_options = {x_unit: 30, y_unit: 60, words_count: nil, icd_mode: :discrete, threshold_lines:
|
81
|
+
default_options = {x_unit: 30, y_unit: 60, words_count: nil, icd_mode: :discrete, threshold_lines: false, scheme: 'nucl_simpa', logo_shift: 300, text_size: 24}
|
61
82
|
cli = SequenceLogo::CLI.new(default_options)
|
62
83
|
cli.instance_eval do
|
63
84
|
parser.banner = doc
|
@@ -78,25 +99,25 @@ begin
|
|
78
99
|
output_file = argv.shift
|
79
100
|
raise ArgumentError, 'Specify output file' unless output_file
|
80
101
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
elsif !$stdin.tty?
|
85
|
-
alignment_infos = $stdin.readlines
|
86
|
-
else
|
87
|
-
raise ArgumentError, 'Specify alignment infos'
|
88
|
-
end
|
102
|
+
alignment_lines = readlines_from_file_or_stdin(argv, source_not_given_msg: 'Specify alignment infos',
|
103
|
+
both_sources_given_msg: 'You can specify alignment infos either from file or from stdin. Don\'t use both sources simultaneously')
|
104
|
+
alignment = make_logo_alignment(load_alignment_infos(alignment_lines), options)
|
89
105
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
106
|
+
scheme_dir = File.join(SequenceLogo::AssetsPath, options[:scheme])
|
107
|
+
letter_images = SequenceLogo::CanvasFactory.letter_images(scheme_dir)
|
108
|
+
canvas_factory = SequenceLogo::CanvasFactory.new(letter_images, x_unit: options[:x_unit], y_unit: options[:y_unit],
|
109
|
+
text_size: options[:text_size], logo_shift: options[:logo_shift])
|
110
|
+
|
111
|
+
case total_orientation
|
112
|
+
when :direct
|
113
|
+
alignment.render(canvas_factory).write('PNG:' + output_file)
|
114
|
+
when :revcomp
|
115
|
+
alignment.revcomp.render(canvas_factory).write('PNG:' + output_file)
|
116
|
+
when :both
|
117
|
+
alignment.render(canvas_factory).write('PNG:' + direct_output_filename(output_file))
|
118
|
+
alignment.revcomp.render(canvas_factory).write('PNG:' + reverse_output_filename(output_file))
|
98
119
|
end
|
99
120
|
|
100
121
|
rescue => err
|
101
122
|
$stderr.puts "\n#{err}\n#{err.backtrace.first(5).join("\n")}\n\nUse --help option for help\n\n#{doc}"
|
102
|
-
end
|
123
|
+
end
|
@@ -1,17 +1,50 @@
|
|
1
1
|
require_relative '../../sequence_logo'
|
2
2
|
require 'shellwords'
|
3
3
|
|
4
|
+
# [{renderable: , name: }] --> [{renderable: , filename: }]
|
5
|
+
def in_necessary_orientations(objects_to_render, orientation, logo_folder)
|
6
|
+
objects_to_render.map do |infos|
|
7
|
+
case orientation
|
8
|
+
when :direct
|
9
|
+
{renderable: infos[:renderable], filename: File.join(logo_folder, "#{infos[:name]}.png") }
|
10
|
+
when :revcomp
|
11
|
+
{renderable: infos[:renderable].revcomp, filename: File.join(logo_folder, "#{infos[:name]}.png") }
|
12
|
+
when :both
|
13
|
+
[ {renderable: infos[:renderable], filename: File.join(logo_folder, "#{infos[:name]}_direct.png") },
|
14
|
+
{renderable: infos[:renderable].revcomp, filename: File.join(logo_folder, "#{infos[:name]}_revcomp.png") } ]
|
15
|
+
end
|
16
|
+
end.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
def arglist_augmented_with_stdin(argv)
|
20
|
+
result = argv
|
21
|
+
result += $stdin.read.shellsplit unless $stdin.tty?
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
4
25
|
begin
|
26
|
+
include SequenceLogo
|
27
|
+
|
5
28
|
doc = <<-EOS
|
6
|
-
sequence_logo is a tool for drawing motif
|
29
|
+
sequence_logo is a tool for drawing motif and sequence logos
|
30
|
+
It is able to process
|
31
|
+
- PCM / PPM format i.e. position count/frequency matrix (*.pat or *.pcm) - preferable
|
32
|
+
- FASTA format (file extensions: .mfa, .fasta, .plain)
|
33
|
+
- SMall BiSMark format (.xml)
|
34
|
+
- IUPAC format (any other extension)
|
7
35
|
Usage:
|
8
|
-
sequence_logo [options] <
|
36
|
+
sequence_logo [options] <motif file>...
|
9
37
|
or
|
10
38
|
ls pcm_folder/*.pcm | sequence_logo [options]
|
39
|
+
or
|
40
|
+
sequence_logo --sequence <sequence>...
|
41
|
+
or
|
42
|
+
sequence_logo --snp-sequence <sequence with SNP>...
|
43
|
+
|
11
44
|
EOS
|
12
45
|
|
13
46
|
argv = ARGV
|
14
|
-
default_options = {x_unit: 30, y_unit: 60, words_count: nil, orientation: :
|
47
|
+
default_options = {x_unit: 30, y_unit: 60, words_count: nil, orientation: :direct, logo_folder: '.', icd_mode: :discrete, threshold_lines: true, scheme: 'nucl_simpa'}
|
15
48
|
cli = SequenceLogo::CLI.new(default_options)
|
16
49
|
cli.instance_eval do
|
17
50
|
parser.banner = doc
|
@@ -23,30 +56,61 @@ begin
|
|
23
56
|
raise ArgumentError, 'Orientation can be either direct or revcomp or both' unless [:direct, :revcomp, :both].include?(v)
|
24
57
|
options[:orientation] = v
|
25
58
|
end
|
59
|
+
|
60
|
+
parser.on('--snp-sequence', 'Specify sequences with SNP (like ATCTC[C/G]CCTAAT) instead of motif filenames') do
|
61
|
+
options[:sequence_w_snp] = true
|
62
|
+
end
|
63
|
+
parser.on('--sequence', 'Specify sequence (like ATCTCGCCTAAT) instead of motif filenames') do
|
64
|
+
options[:sequence] = true
|
65
|
+
end
|
26
66
|
end
|
27
67
|
options = cli.parse_options!(argv)
|
28
68
|
|
29
69
|
logo_folder = options[:logo_folder]
|
30
70
|
Dir.mkdir(logo_folder) unless Dir.exist?(logo_folder)
|
31
71
|
|
32
|
-
|
33
|
-
|
34
|
-
|
72
|
+
scheme_dir = File.join(SequenceLogo::AssetsPath, options[:scheme])
|
73
|
+
letter_images = SequenceLogo::CanvasFactory.letter_images(scheme_dir)
|
74
|
+
canvas_factory = SequenceLogo::CanvasFactory.new(letter_images, x_unit: options[:x_unit], y_unit: options[:y_unit])
|
75
|
+
|
76
|
+
raise "Specify either sequence or sequence with SNP or none of them, but not both" if options[:sequence] && options[:sequence_w_snp]
|
35
77
|
|
36
|
-
|
37
|
-
|
38
|
-
|
78
|
+
objects_to_render = []
|
79
|
+
if options[:sequence]
|
80
|
+
sequences = arglist_augmented_with_stdin(argv)
|
81
|
+
raise ArgumentError, 'Specify at least one sequence' if sequences.empty?
|
82
|
+
|
83
|
+
sequences.each do |sequence|
|
84
|
+
objects_to_render << {renderable: SequenceLogo::Sequence.new(sequence),
|
85
|
+
name: File.join(logo_folder, sequence)}
|
86
|
+
end
|
87
|
+
elsif options[:sequence_w_snp]
|
88
|
+
sequences = arglist_augmented_with_stdin(argv)
|
89
|
+
raise ArgumentError, 'Specify at least one sequence' if sequences.empty?
|
39
90
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
SequenceLogo.draw_logo(ppm, options).write(direct_output)
|
91
|
+
sequences.each do |sequence_w_snp|
|
92
|
+
objects_to_render << {renderable: SequenceLogo::SequenceWithSNP.from_string(sequence_w_snp),
|
93
|
+
name: File.join(logo_folder, sequence_w_snp.gsub(/[\[\]\/]/, '_'))}
|
44
94
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
95
|
+
else
|
96
|
+
filenames = arglist_augmented_with_stdin(argv)
|
97
|
+
raise ArgumentError, 'Specify at least one motif file' if filenames.empty?
|
98
|
+
|
99
|
+
filenames.each do |filename|
|
100
|
+
ppm = get_ppm_from_file(filename)
|
101
|
+
checkerr("bad input file: #{filename}") { ppm == nil }
|
102
|
+
|
103
|
+
logo = SequenceLogo::PPMLogo.new( ppm,
|
104
|
+
icd_mode: options[:icd_mode],
|
105
|
+
words_count: options[:words_count],
|
106
|
+
enable_threshold_lines: options[:threshold_lines])
|
107
|
+
objects_to_render << {renderable: logo, name: File.join(logo_folder, File.basename_wo_extname(filename))}
|
48
108
|
end
|
49
109
|
end
|
110
|
+
|
111
|
+
in_necessary_orientations(objects_to_render, options[:orientation], logo_folder).each do |infos|
|
112
|
+
infos[:renderable].render(canvas_factory).write("PNG:#{infos[:filename]}")
|
113
|
+
end
|
50
114
|
rescue => err
|
51
115
|
$stderr.puts "\n#{err}\n#{err.backtrace.first(5).join("\n")}\n\nUse --help option for help\n\n#{doc}"
|
52
|
-
end
|
116
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
class Magick::ImageList
|
4
|
+
def put_image_at(image, x, y)
|
5
|
+
self << image
|
6
|
+
cur_image.page = Magick::Rectangle.new(0, 0, x, y)
|
7
|
+
end
|
8
|
+
|
9
|
+
# add transparent layer so that full canvas size can't be less than given size
|
10
|
+
def set_minimal_size(x_size, y_size)
|
11
|
+
empty_image = Magick::Image.new(x_size, y_size){ self.background_color = 'transparent'}
|
12
|
+
self.unshift(empty_image)
|
13
|
+
end
|
14
|
+
end
|
@@ -1,114 +1,5 @@
|
|
1
|
+
require_relative 'canvases'
|
2
|
+
require_relative 'canvas_factory'
|
3
|
+
require_relative 'alignment'
|
4
|
+
require_relative 'data_models'
|
1
5
|
require_relative 'ytilib'
|
2
|
-
require 'RMagick'
|
3
|
-
|
4
|
-
module SequenceLogo
|
5
|
-
def self.draw_threshold_lines(i_logo, ppm)
|
6
|
-
x_size = i_logo.columns
|
7
|
-
y_size = i_logo.rows
|
8
|
-
|
9
|
-
line2of4 = y_size - ppm.get_line(ppm.icd2of4) * y_size
|
10
|
-
lineThc = y_size - ppm.get_line(ppm.icdThc) * y_size
|
11
|
-
lineTlc = y_size - ppm.get_line(ppm.icdTlc) * y_size
|
12
|
-
|
13
|
-
dr = Magick::Draw.new
|
14
|
-
dr.fill('transparent')
|
15
|
-
|
16
|
-
dr.stroke_width(y_size / 200.0)
|
17
|
-
dr.stroke_dasharray(7,7)
|
18
|
-
|
19
|
-
dr.stroke('silver')
|
20
|
-
dr.line(0, line2of4, x_size, line2of4)
|
21
|
-
dr.line(0, lineThc, x_size, lineThc)
|
22
|
-
dr.line(0, lineTlc, x_size, lineTlc)
|
23
|
-
|
24
|
-
dr.draw(i_logo)
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.create_canvas(ppm, options)
|
28
|
-
x_size = options[:x_unit] * ppm.length
|
29
|
-
y_size = options[:y_unit]
|
30
|
-
|
31
|
-
i_logo = Magick::ImageList.new
|
32
|
-
if options[:icd_mode] == :discrete
|
33
|
-
i_logo.new_image(x_size, y_size, Magick::HatchFill.new('white', 'white'))
|
34
|
-
draw_threshold_lines(i_logo, ppm) if options[:threshold_lines]
|
35
|
-
else
|
36
|
-
i_logo.new_image(x_size, y_size, Magick::HatchFill.new('white', 'bisque'))
|
37
|
-
end
|
38
|
-
|
39
|
-
i_logo
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.letter_images(scheme_dir)
|
43
|
-
if File.exist?(File.join(scheme_dir,'a.png'))
|
44
|
-
extension = 'png'
|
45
|
-
elsif File.exist?(File.join(scheme_dir,'a.gif'))
|
46
|
-
extension = 'gif'
|
47
|
-
else
|
48
|
-
raise "Scheme not exists in folder #{scheme_dir}"
|
49
|
-
end
|
50
|
-
|
51
|
-
letter_files = %w[a c g t].collect{|letter| File.join(scheme_dir, "#{letter}.#{extension}") }
|
52
|
-
Magick::ImageList.new(*letter_files)
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.draw_letters_on_canvas(i_logo, i_letters, ppm, options)
|
56
|
-
y_unit = options[:y_unit]
|
57
|
-
x_unit = options[:x_unit]
|
58
|
-
matrix = ppm.get_logo(options[:icd_mode])
|
59
|
-
matrix['A'].each_index { |i|
|
60
|
-
y_pos = 0
|
61
|
-
sorted_letters = ['A', 'C', 'G', 'T'].collect { |letter| {:score => matrix[letter][i], :letter => letter} }.sort_by { |pair| pair[:score] }.collect { |pair| pair[:letter] }.reverse
|
62
|
-
sorted_letters.each { |letter|
|
63
|
-
next if y_unit * matrix[letter][i] <= 1
|
64
|
-
letter_index = {'A' => 0, 'C' => 1, 'G' => 2, 'T' => 3}[letter]
|
65
|
-
y_block = (y_unit * matrix[letter][i]).round
|
66
|
-
i_logo << i_letters[letter_index].dup.resize(x_unit, y_block)
|
67
|
-
y_pos += y_block
|
68
|
-
i_logo.cur_image.page = Magick::Rectangle.new(0, 0, i * x_unit, y_unit - y_pos )
|
69
|
-
}
|
70
|
-
}
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.draw_logo(ppm, options = {})
|
74
|
-
ppm.words_count = options[:words_count] if options[:words_count]
|
75
|
-
unless ppm.words_count
|
76
|
-
report "words count for PPM is undefined, assuming weblogo mode"
|
77
|
-
options[:icd_mode] = :weblogo
|
78
|
-
end
|
79
|
-
i_logo = create_canvas(ppm, options)
|
80
|
-
scheme_dir = File.join(AssetsPath, options[:scheme])
|
81
|
-
draw_letters_on_canvas(i_logo, letter_images(scheme_dir), ppm, options)
|
82
|
-
i_logo = i_logo.flatten_images
|
83
|
-
end
|
84
|
-
|
85
|
-
# logos = { filename => {shift: ..., length: ..., name: ...} }
|
86
|
-
def self.glue_files(logos, output_file, options)
|
87
|
-
logo_shift = options[:logo_shift] || 300
|
88
|
-
x_unit = options[:x_unit] || 30
|
89
|
-
y_unit = options[:y_unit] || 60
|
90
|
-
text_size = options[:text_size] || 24
|
91
|
-
|
92
|
-
leftmost_shift = logos.map{|file,infos| infos[:shift] }.min
|
93
|
-
logos.each{|file, infos| infos[:shift] -= leftmost_shift}
|
94
|
-
full_alignment_size = logos.map{|file,infos| infos[:length] + infos[:shift] }.max
|
95
|
-
|
96
|
-
x_size = logo_shift + full_alignment_size * x_unit
|
97
|
-
y_size = logos.size * y_unit
|
98
|
-
command_string = "convert -size #{ x_size }x#{ y_size } -pointsize #{text_size} xc:white "
|
99
|
-
logos.each_with_index do |(logo_filename,infos), idx|
|
100
|
-
logo_x_start = logo_shift + infos[:shift] * x_unit
|
101
|
-
logo_y_start = y_unit * idx
|
102
|
-
command_string << "\"#{ logo_filename }\" -geometry +#{ logo_x_start }+#{ logo_y_start } -composite "
|
103
|
-
end
|
104
|
-
|
105
|
-
command_draw_names = ""
|
106
|
-
logos.each_with_index do |(logo_filename,infos), idx|
|
107
|
-
text_x_start = 10
|
108
|
-
text_y_start = y_unit * (idx + 0.5)
|
109
|
-
command_draw_names << "-draw \"text #{ text_x_start },#{ text_y_start } '#{infos[:name]}'\" "
|
110
|
-
end
|
111
|
-
|
112
|
-
system(command_string + command_draw_names + "\"#{output_file}\"")
|
113
|
-
end
|
114
|
-
end
|
data/lib/sequence_logo/ytilib.rb
CHANGED
@@ -5,6 +5,18 @@ module Ytilib
|
|
5
5
|
attr_accessor :words_count
|
6
6
|
|
7
7
|
alias length size
|
8
|
+
|
9
|
+
def each_position_index(&block)
|
10
|
+
@matrix['A'].each_index(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def each_position(&block)
|
14
|
+
return enum_for(:each_position) unless block_given?
|
15
|
+
@matrix['A'].each_index do |i|
|
16
|
+
position = ['A', 'C', 'G', 'T'].map{|letter| @matrix[letter][i] }
|
17
|
+
yield position
|
18
|
+
end
|
19
|
+
end
|
8
20
|
|
9
21
|
def score_mean(bckgr = Randoom::DEF_PROBS)
|
10
22
|
(0...@size).inject(0.0) { |mean, i| mean += ['A','C','G','T'].inject(0.0) { |sum,l| sum += @matrix[l][i] * bckgr[l] } }
|
@@ -324,12 +336,12 @@ module Ytilib
|
|
324
336
|
attributes = {"length" => @size}
|
325
337
|
attributes["words-count"] = @words_count if @words_count && @words_count > 0
|
326
338
|
pe = b.add_element( pwm ? "PWM" : "PCM", attributes )
|
327
|
-
|
339
|
+
each_position_index do |i|
|
328
340
|
pm_c = pe.add_element("pm-column", {"position" => i+1})
|
329
341
|
['A', 'C', 'G', 'T'].each { |l|
|
330
342
|
pm_c.add_element(l.downcase).add_text(@matrix[l][i].to_s)
|
331
343
|
}
|
332
|
-
|
344
|
+
end
|
333
345
|
end
|
334
346
|
|
335
347
|
def PM.from_bismark(b, iupacomp = false)
|
@@ -43,40 +43,25 @@ class PPM
|
|
43
43
|
|
44
44
|
|
45
45
|
def get_logo_weblogo
|
46
|
-
rseq =
|
47
|
-
|
48
|
-
rseq << 2 + ['A','C','G','T'].inject(0) { |sum, l|
|
49
|
-
pn = @matrix[l][i]
|
50
|
-
sum += (pn == 0) ? 0 : pn * Math.log(pn) / Math.log(2)
|
51
|
-
}
|
46
|
+
rseq = each_position.map {|position|
|
47
|
+
position.map{|el| (el == 0) ? 0 : el * Math.log2(el) }.inject(0, :+) + 2
|
52
48
|
}
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
['A','C','G','T'].each { |l|
|
57
|
-
mat[l][i]= @matrix[l][i] * rseq[i] / 2 # so we can handle a '2 bit' scale here
|
58
|
-
}
|
49
|
+
|
50
|
+
each_position.with_index.map {|position, ind|
|
51
|
+
position.map{|el| el * rseq[ind] / 2 }
|
59
52
|
}
|
60
|
-
|
61
|
-
mat
|
62
53
|
end
|
63
54
|
|
64
55
|
def get_logo_discrete
|
65
56
|
checkerr("words count is undefined") { !words_count }
|
66
57
|
|
67
|
-
rseq =
|
68
|
-
|
69
|
-
rseq << (icd4of4 == 0 ? 1.0 : ( (infocod(i) - icd4of4) / icd4of4 ).abs)
|
58
|
+
rseq = each_position_index.map {|i|
|
59
|
+
(icd4of4 == 0) ? 1.0 : get_line(infocod(i))
|
70
60
|
}
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
['A','C','G','T'].each { |l|
|
75
|
-
mat[l][i] = @matrix[l][i] * rseq[i]
|
76
|
-
}
|
61
|
+
|
62
|
+
each_position.with_index.map {|position, ind|
|
63
|
+
position.map{|el| el * rseq[ind] }
|
77
64
|
}
|
78
|
-
|
79
|
-
mat
|
80
65
|
end
|
81
66
|
|
82
67
|
def revcomp
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequence_logo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Vorontsov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rmagick
|
@@ -44,14 +44,32 @@ files:
|
|
44
44
|
- bin/glue_logos
|
45
45
|
- bin/sequence_logo
|
46
46
|
- lib/sequence_logo.rb
|
47
|
+
- lib/sequence_logo/alignment.rb
|
47
48
|
- lib/sequence_logo/assets/nucl_simpa/a.png
|
48
49
|
- lib/sequence_logo/assets/nucl_simpa/c.png
|
49
50
|
- lib/sequence_logo/assets/nucl_simpa/g.png
|
50
51
|
- lib/sequence_logo/assets/nucl_simpa/t.png
|
52
|
+
- lib/sequence_logo/assets/nucl_simpa_bw/a.png
|
53
|
+
- lib/sequence_logo/assets/nucl_simpa_bw/c.png
|
54
|
+
- lib/sequence_logo/assets/nucl_simpa_bw/g.png
|
55
|
+
- lib/sequence_logo/assets/nucl_simpa_bw/t.png
|
56
|
+
- lib/sequence_logo/canvas_factory.rb
|
57
|
+
- lib/sequence_logo/canvases.rb
|
58
|
+
- lib/sequence_logo/canvases/gluing_canvas.rb
|
59
|
+
- lib/sequence_logo/canvases/horizontal_gluing_canvas.rb
|
60
|
+
- lib/sequence_logo/canvases/logo_canvas.rb
|
61
|
+
- lib/sequence_logo/canvases/vertical_gluing_canvas.rb
|
51
62
|
- lib/sequence_logo/cli.rb
|
63
|
+
- lib/sequence_logo/data_models.rb
|
64
|
+
- lib/sequence_logo/data_models/ppm_logo.rb
|
65
|
+
- lib/sequence_logo/data_models/predefined_logo.rb
|
66
|
+
- lib/sequence_logo/data_models/sequence.rb
|
67
|
+
- lib/sequence_logo/data_models/sequence_with_snp.rb
|
52
68
|
- lib/sequence_logo/exec/glue_logos.rb
|
53
69
|
- lib/sequence_logo/exec/sequence_logo.rb
|
70
|
+
- lib/sequence_logo/magick_support.rb
|
54
71
|
- lib/sequence_logo/pmflogo_lib.rb
|
72
|
+
- lib/sequence_logo/support.rb
|
55
73
|
- lib/sequence_logo/version.rb
|
56
74
|
- lib/sequence_logo/ytilib.rb
|
57
75
|
- lib/sequence_logo/ytilib/addon.rb
|
@@ -90,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
108
|
version: '0'
|
91
109
|
requirements: []
|
92
110
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.2.
|
111
|
+
rubygems_version: 2.2.2
|
94
112
|
signing_key:
|
95
113
|
specification_version: 4
|
96
114
|
summary: Tool for drawing sequence logos of motifs
|