iroki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ # Copyright 2016 Ryan Moore
2
+ # Contact: moorer@udel.edu
3
+ #
4
+ # This file is part of Iroki.
5
+ #
6
+ # Iroki is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Iroki is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Iroki. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Iroki
20
+ module CoreExt
21
+ module File
22
+ def check_file arg, which
23
+ help = " Try iroki --help for help."
24
+
25
+ abort_if arg.nil?,
26
+ "You must provide a #{which} file.#{help}"
27
+
28
+ abort_unless Object::File.exists?(arg),
29
+ "The file #{arg} doesn't exist.#{help}"
30
+
31
+ arg
32
+ end
33
+
34
+ def parse_name_map fname
35
+ check_file fname, :name_map
36
+
37
+ name_map = {}
38
+ Object::File.open(fname).each_line do |line|
39
+ oldname, newname = line.chomp.split "\t"
40
+
41
+
42
+ abort_if oldname.nil? || oldname.empty?,
43
+ "Column 1 missing for line: #{line.inspect}"
44
+
45
+ abort_if newname.nil? || newname.empty?,
46
+ "Column 2 missing for line: #{line.inspect}"
47
+
48
+ oldname = clean oldname
49
+ newname = clean newname
50
+
51
+ abort_if name_map.has_key?(oldname),
52
+ "#{oldname} is repeated in column 1"
53
+
54
+ name_map[oldname] = newname
55
+ end
56
+
57
+ abort_if duplicate_values?(name_map),
58
+ "Names in column 2 of name map file must be unique"
59
+
60
+ name_map
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ # Copyright 2016 Ryan Moore
2
+ # Contact: moorer@udel.edu
3
+ #
4
+ # This file is part of Iroki.
5
+ #
6
+ # Iroki is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Iroki is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Iroki. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Iroki
20
+ module CoreExt
21
+ module Hash
22
+ def duplicate_values? hash
23
+ values = hash.values
24
+ values.count != 1 && values.count != values.uniq.count
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ module Iroki
2
+ module CoreExt
3
+ module String
4
+
5
+ def hex? str
6
+ str.match(/^#[0-9A-Fa-f]{6}$/)
7
+ end
8
+
9
+ def clean str
10
+ str.gsub(/[^\p{Alnum}_]+/, "_").gsub(/_+/, "_")
11
+ end
12
+
13
+ def has_color? name
14
+ name.match(/(.*)(\[&!color="#[0-9A-Fa-f]{6}"\])/)
15
+ end
16
+ alias already_checked? has_color?
17
+
18
+ def clean_name name
19
+ if name.nil?
20
+ nil
21
+ else
22
+ if (match = has_color? name)
23
+ name = match[1]
24
+ color = match[2]
25
+
26
+ clean(name) + color
27
+ else
28
+ clean(name)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,232 @@
1
+ # Copyright 2016 Ryan Moore
2
+ # Contact: moorer@udel.edu
3
+ #
4
+ # This file is part of Iroki.
5
+ #
6
+ # Iroki is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Iroki is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Iroki. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require "bio"
20
+ require "set"
21
+
22
+ module Iroki
23
+ module Main
24
+ def self.main(color_branches: nil,
25
+ color_taxa_names: nil,
26
+ exact: nil,
27
+ remove_bootstraps_below: nil,
28
+ color_map_f: nil,
29
+ name_map_f: nil,
30
+ auto_color: nil,
31
+ display_auto_color_options: nil,
32
+ newick_f: nil,
33
+ out_f: nil)
34
+
35
+
36
+
37
+ if display_auto_color_options
38
+ puts "\n Choices for --auto-color ..."
39
+ print " basic, basic_light, basic_dark, funky, funky_light, " +
40
+ "funky_dark\n\n"
41
+ exit
42
+ end
43
+
44
+ auto_color_options =
45
+ ["basic", "basic_light", "basic_dark",
46
+ "funky", "funky_light", "funky_dark",]
47
+
48
+
49
+ if(!auto_color.nil? &&
50
+ !auto_color_options.include?(auto_color))
51
+ puts "\n Choices for --auto-color ..."
52
+ print " basic, basic_light, basic_dark, funky, funky_light, " +
53
+ "funky_dark\n\n"
54
+
55
+ Trollop.die :auto_color, "#{auto_color} is not a valid option"
56
+ end
57
+
58
+ case auto_color
59
+ when nil
60
+ auto_colors = BASIC
61
+ when "basic"
62
+ auto_colors = BASIC
63
+ when "basic_light"
64
+ auto_colors = BASIC_LIGHT
65
+ when "basic_dark"
66
+ auto_colors = BASIC_DARK
67
+ when "funky"
68
+ auto_colors = FUNKY
69
+ when "funky_light"
70
+ auto_colors = FUNKY_LIGHT
71
+ when "funky_dark"
72
+ auto_colors = FUNKY_DARK
73
+ end
74
+
75
+ # color_branches = true
76
+ # color_taxa_names = true
77
+ # exact = false
78
+ # color_map_f = "test_files/500.patterns_with_name_map"
79
+ # name_map_f = "test_files/500.name_map"
80
+ # ARGV[0] = "test_files/500.zetas.tre"
81
+ newick = check_file newick_f, :newick
82
+
83
+ color_f = nil
84
+ if color_taxa_names || color_branches
85
+ color_f = check_file color_map_f, :color_map_f
86
+ end
87
+
88
+ check = color_map_f &&
89
+ !color_taxa_names &&
90
+ !color_branches
91
+
92
+ abort_if check,
93
+ "A pattern file was provided without specifying " +
94
+ "any coloring options"
95
+
96
+
97
+ # if passed color other than one defined, return black
98
+ black = "#000000"
99
+ red = "#FF1300"
100
+ yellow = "#FFD700"
101
+ blue = "#5311FF"
102
+ green = "#00FF2C"
103
+ color2hex = Hash.new "[&!color=\"#{black}\"]"
104
+ color2hex.merge!({
105
+ "black" => "[&!color=\"#{black}\"]",
106
+ "red" => "[&!color=\"#{red}\"]",
107
+ "blue" => "[&!color=\"#{blue}\"]",
108
+ "yellow" => "[&!color=\"#{yellow}\"]",
109
+ "green" => "[&!color=\"#{green}\"]"
110
+ })
111
+
112
+ # check if complementary colors requested
113
+ if color_f
114
+ colors = Set.new
115
+ File.open(color_f).each_line do |line|
116
+ _, color = line.chomp.split "\t"
117
+
118
+ colors << color
119
+ end
120
+
121
+ auto_color = colors.all? { |color| color.match /\A[0-4]\Z/ }
122
+ end
123
+
124
+ # get the color patterns
125
+ if color_f
126
+ patterns = {}
127
+ File.open(color_f).each_line do |line|
128
+ pattern, color = line.chomp.split "\t"
129
+
130
+ color = "black" if color.nil? || color.empty?
131
+
132
+ if name_map_f || color_taxa_names || color_branches
133
+ pattern = clean_name pattern
134
+ end
135
+
136
+ if !exact
137
+ pattern = Regexp.new pattern
138
+ end
139
+
140
+ if auto_color
141
+ patterns[pattern] = "[&!color=\"#{auto_colors[color]}\"]"
142
+ else
143
+ if hex? color
144
+ patterns[pattern] = "[&!color=\"#{color}\"]"
145
+ else
146
+ patterns[pattern] = color2hex[color]
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ treeio = Bio::FlatFile.open(Bio::Newick, newick)
153
+
154
+ newick = treeio.next_entry
155
+ tree = newick.tree
156
+
157
+ # do this first cos everything after will use the "new" names
158
+ if name_map_f
159
+ name_map = parse_name_map name_map_f
160
+
161
+ tree.collect_node! do |node|
162
+ unless node.name.nil?
163
+ # every name is cleaned no matter what
164
+ node.name = clean node.name
165
+
166
+ if name_map.has_key?(node.name)
167
+ node.name = name_map[node.name]
168
+ end
169
+ end
170
+
171
+ node
172
+ end
173
+ end
174
+
175
+ if color_taxa_names
176
+ leaves = tree.leaves.map do |n|
177
+ name = clean_name n.name
178
+
179
+ if (color = add_color_to_leaf_branch(patterns, name, exact))
180
+ name + color
181
+ else
182
+ name
183
+ end
184
+ end
185
+ else
186
+ leaves = tree.leaves.map { |n| clean_name n.name }
187
+ end
188
+
189
+ if color_branches
190
+ total = tree.nodes.count
191
+ n = 0
192
+ tree.collect_node! do |node|
193
+ n += 1
194
+ $stderr.printf "Node: %d of %d\r", n, total
195
+
196
+ color_nodes patterns, tree, node, exact
197
+ end
198
+ end
199
+ $stderr.puts
200
+
201
+ if remove_bootstraps_below
202
+ tree.collect_node! do |node|
203
+ if node.bootstrap && node.bootstrap < remove_bootstraps_below
204
+ node.bootstrap_string = ""
205
+ end
206
+
207
+ node
208
+ end
209
+ end
210
+
211
+
212
+
213
+ tre_str = tree.newick(indent: false).gsub(/'/, '')
214
+
215
+ nexus = "#NEXUS
216
+ begin taxa;
217
+ dimensions ntax=#{leaves.count};
218
+ taxlabels
219
+ #{leaves.join("\n")}
220
+ ;
221
+ end;
222
+
223
+ begin trees;
224
+ tree tree_1 = [&R] #{tre_str}
225
+ end;
226
+
227
+ #{FIG}"
228
+
229
+ File.open(out_f, "w") { |f| f.puts nexus }
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,103 @@
1
+ # Copyright 2016 Ryan Moore
2
+ # Contact: moorer@udel.edu
3
+ #
4
+ # This file is part of Iroki.
5
+ #
6
+ # Iroki is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Iroki is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Iroki. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Iroki
20
+ module Utils
21
+ include AbortIf
22
+ include Iroki::CoreExt::String
23
+
24
+ def leaf? tree, node
25
+ tree.children(node).empty?
26
+ end
27
+
28
+ def add_color_to_leaf_branch patterns, node, exact
29
+ num_matches = 0
30
+ color = nil
31
+ already_matched = false
32
+
33
+ if exact # treat patterns as string matching
34
+ node_s = node.to_s
35
+ if patterns.has_key? node_s
36
+ color = patterns[node_s]
37
+
38
+ return color
39
+ else
40
+ return nil
41
+ end
42
+ else
43
+ node_s = node.to_s
44
+
45
+ patterns.each do |pattern, this_color|
46
+ if node_s =~ pattern
47
+ abort_if already_matched,
48
+ "Non specific matching for #{node_s}"
49
+
50
+ color = this_color
51
+ already_matched = true
52
+ end
53
+ end
54
+
55
+ return color
56
+ end
57
+ end
58
+
59
+ def get_color node
60
+ begin
61
+ node.name.match(/\[&!color="#[0-9A-Fa-f]{6}"\]/)[0]
62
+ rescue NoMethodError => e
63
+ nil
64
+ end
65
+ end
66
+
67
+ def color_nodes patterns, tree, node, exact
68
+ # # check if it needs color, if so set the color
69
+ # color = add_color_to_leaf_branch patterns, node, exact
70
+
71
+ # clean the name no matter what
72
+ node.name = clean_name node.name
73
+
74
+ # if its a leaf that hasnt been checked & needs color
75
+ if leaf?(tree, node) && !already_checked?(node.name) # && color
76
+ # check if it needs color, if so set the color
77
+
78
+ # NOTE: this was originally before cleaning the node name a
79
+ # couple lines up, does it matter that it is after?
80
+ color = add_color_to_leaf_branch patterns, node, exact
81
+
82
+ # add color to the name
83
+ node.name = node.name + color if color
84
+ elsif !leaf?(tree, node)
85
+ children = tree.children(node) # get the children
86
+ children_colors = []
87
+ children.each do |child|
88
+ # recurse to color the child if needed
89
+ color_nodes patterns, tree, child, exact
90
+ children_colors << get_color(child) # add color of the child
91
+ end
92
+
93
+ # if all the children have the same color
94
+ if children_colors.uniq.count == 1
95
+ # set the branch node to only the color name
96
+ node.name = children_colors[0]
97
+ end
98
+ end
99
+
100
+ return node
101
+ end
102
+ end
103
+ end