iroki 0.0.6 → 0.0.7
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 +4 -4
- data/.gitignore +2 -0
- data/README.md +4 -0
- data/exe/newick_to_phyloxml_color +132 -0
- data/exe/reorder_nodes +206 -0
- data/iroki.gemspec +1 -0
- data/lib/iroki/version.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e95553e4a9cc4572479c11aa9f5701e897e7fbd9
|
4
|
+
data.tar.gz: ff5b30ae95cdfb53e2c443877100f55cf3846e67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 538e1db4cdb5201363922f342f0a9ee9fd5100bfd59e0ff2d1e480034ac6591bd2355548b569b5011dbdeb3c34a589f3a811569f3bc65312b8bb6774bb45704f
|
7
|
+
data.tar.gz: 363ef46a8ff54ab9858c46d0f6cfdc04f270cb03933ed2378af3a818a9ba3d6ec70a2f1b4d050ebd1da4076f2862eb8a56e2e7e55773bdbccfe1809063c48beb
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# NOTE make sure the newick has no spaces for jsPhyloXML
|
4
|
+
|
5
|
+
# NOTE for Archaeopteryx, you need <name>#{boot}</name> to show
|
6
|
+
# bootsraps, but for jsPhyloXML, just the confidence is needed
|
7
|
+
|
8
|
+
require "set"
|
9
|
+
require "bio"
|
10
|
+
require "color"
|
11
|
+
require "abort_if"
|
12
|
+
|
13
|
+
include AbortIf
|
14
|
+
|
15
|
+
logger.warn { "This is an internal Iroki script. It is not meant for " +
|
16
|
+
"general use." }
|
17
|
+
|
18
|
+
def leaf? tree, node
|
19
|
+
tree.children(node).empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
fname = ARGV.first
|
23
|
+
|
24
|
+
tree_str = File.open(fname, 'rt').read
|
25
|
+
|
26
|
+
newick = Bio::Newick.new(tree_str, parser: :naive)
|
27
|
+
|
28
|
+
$tree = newick.tree
|
29
|
+
|
30
|
+
$xml_start = %q{<phyloxml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.phyloxml.org http://www.phyloxml.org/1.10/phyloxml.xsd" xmlns="http://www.phyloxml.org">
|
31
|
+
<phylogeny rooted="false">
|
32
|
+
<clade>
|
33
|
+
}
|
34
|
+
|
35
|
+
$xml_end = %q{</clade>
|
36
|
+
</phylogeny>
|
37
|
+
</phyloxml>
|
38
|
+
}
|
39
|
+
|
40
|
+
def leaf? tree, node
|
41
|
+
tree.descendents(node).count.zero?
|
42
|
+
end
|
43
|
+
|
44
|
+
def name_and_color node
|
45
|
+
m = node.name.match(/(.*)___ryan_([a-fA-F0-9]+)/)
|
46
|
+
if m
|
47
|
+
name = m[1]
|
48
|
+
hex = m[2]
|
49
|
+
|
50
|
+
col = Color::RGB.by_hex hex
|
51
|
+
|
52
|
+
[name, col]
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def make_xml_string descendents, start_node
|
59
|
+
while (node = descendents.shift)
|
60
|
+
unless $already_added.include? node
|
61
|
+
ary = name_and_color(node)
|
62
|
+
new_name, col = ary
|
63
|
+
|
64
|
+
if leaf? $tree, node
|
65
|
+
# TODO this will raise something if no dist, rescue it
|
66
|
+
dist = $tree.distance(start_node, node)
|
67
|
+
dist = 1
|
68
|
+
|
69
|
+
if ary # then it needs color
|
70
|
+
color_xml = %Q{<color>
|
71
|
+
<red>#{col.red}</red>
|
72
|
+
<green>#{col.green}</green>
|
73
|
+
<blue>#{col.blue}</blue>
|
74
|
+
</color>
|
75
|
+
<property ref="style:font_color" datatype="xsd:token" applies_to="node">##{col.hex}</property>}
|
76
|
+
else
|
77
|
+
color_xml = ""
|
78
|
+
end
|
79
|
+
|
80
|
+
$xml_start << "<clade>
|
81
|
+
<name>#{ary ? new_name : node.name}</name>
|
82
|
+
<branch_length>#{dist}</branch_length>
|
83
|
+
#{color_xml}
|
84
|
+
</clade>\n"
|
85
|
+
$already_added << node
|
86
|
+
$prev_node = node
|
87
|
+
else
|
88
|
+
boot = node.bootstrap
|
89
|
+
if boot
|
90
|
+
boot_xml = %Q{\n<confidence type="bootstrap">#{boot}</confidence>}
|
91
|
+
else
|
92
|
+
boot_xml = ""
|
93
|
+
end
|
94
|
+
|
95
|
+
# TODO this will raise something if no dist, rescue it
|
96
|
+
dist = $tree.distance(start_node, node)
|
97
|
+
dist = 1
|
98
|
+
|
99
|
+
if ary # then it needs color
|
100
|
+
color_xml = "<color>
|
101
|
+
<red>#{col.red}</red>
|
102
|
+
<green>#{col.green}</green>
|
103
|
+
<blue>#{col.blue}</blue>
|
104
|
+
</color>"
|
105
|
+
else
|
106
|
+
color_xml = ""
|
107
|
+
end
|
108
|
+
|
109
|
+
$xml_start << "<clade>
|
110
|
+
<branch_length>#{dist}</branch_length>
|
111
|
+
#{boot_xml}
|
112
|
+
#{color_xml}"
|
113
|
+
|
114
|
+
STDERR.puts "LOG -- recurse"
|
115
|
+
make_xml_string $tree.descendents(node), node
|
116
|
+
$xml_start << "</clade>\n"
|
117
|
+
$already_added << node
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
$already_added = Set.new
|
124
|
+
|
125
|
+
start_node = $tree.root
|
126
|
+
|
127
|
+
$descendents = $tree.descendents(start_node)
|
128
|
+
|
129
|
+
make_xml_string $descendents, start_node
|
130
|
+
|
131
|
+
puts $xml_start
|
132
|
+
puts "#{$xml_end}"
|
data/exe/reorder_nodes
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bio"
|
4
|
+
require "trollop"
|
5
|
+
require "abort_if"
|
6
|
+
|
7
|
+
include AbortIf
|
8
|
+
|
9
|
+
module Math
|
10
|
+
def self.max ary
|
11
|
+
ary.max
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.median ary
|
15
|
+
len = ary.length
|
16
|
+
if len > 0
|
17
|
+
sorted = ary.sort
|
18
|
+
|
19
|
+
if len.odd?
|
20
|
+
sorted[len / 2]
|
21
|
+
else
|
22
|
+
(sorted[(len-1) / 2] + sorted[len/2]) / 2.0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.mean ary
|
28
|
+
len = ary.length
|
29
|
+
if len > 0
|
30
|
+
ary.reduce(:+) / len.to_f
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.dist_for_comp metric, ary
|
35
|
+
self.send(metric, ary)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class Bio::Tree
|
41
|
+
def root_to_node_dist node
|
42
|
+
self.distance(self.root, node)
|
43
|
+
end
|
44
|
+
|
45
|
+
def root_to_node_dists nodes
|
46
|
+
nodes.map do |node|
|
47
|
+
root_to_node_dist node
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def __to_newick(parents, source, depth, format_leaf,
|
52
|
+
options, &block)
|
53
|
+
result = []
|
54
|
+
if indent_string = __get_option(:indent, options) then
|
55
|
+
indent0 = indent_string * depth
|
56
|
+
indent = indent_string * (depth + 1)
|
57
|
+
newline = "\n"
|
58
|
+
else
|
59
|
+
indent0 = indent = newline = ''
|
60
|
+
end
|
61
|
+
out_edges = self.out_edges(source)
|
62
|
+
if block_given? then
|
63
|
+
out_edges.sort! { |edge1, edge2| yield(edge1[1], edge2[1]) }
|
64
|
+
else
|
65
|
+
# this sorts within clades
|
66
|
+
out_edges.sort! do |edge1, edge2|
|
67
|
+
n1 = edge1[1]
|
68
|
+
n2 = edge2[1]
|
69
|
+
# o1 = edge1[1].order_number
|
70
|
+
# o2 = edge2[1].order_number
|
71
|
+
d1 = edge1[2].distance
|
72
|
+
d2 = edge2[2].distance
|
73
|
+
# STDERR.puts
|
74
|
+
# STDERR.puts [:hi, edge1[0], edge1[1], d1, edge2[0], edge2[1], d2].inspect
|
75
|
+
|
76
|
+
n1_leaves = self.leaves(n1)
|
77
|
+
n2_leaves = self.leaves(n2)
|
78
|
+
|
79
|
+
dists_1 = root_to_node_dists n1_leaves
|
80
|
+
dists_2 = root_to_node_dists n2_leaves
|
81
|
+
|
82
|
+
the_dist_1 = Math.dist_for_comp COMP_METHOD, dists_1
|
83
|
+
the_dist_2 = Math.dist_for_comp COMP_METHOD, dists_2
|
84
|
+
|
85
|
+
# TODO check if leaves is empty
|
86
|
+
if the_dist_1 && the_dist_2
|
87
|
+
# STDERR.puts [:bye, the_dist_1, the_dist_2]
|
88
|
+
if LONG_ON_TOP
|
89
|
+
the_dist_2 <=> the_dist_1
|
90
|
+
else
|
91
|
+
the_dist_1 <=> the_dist_2
|
92
|
+
end
|
93
|
+
elsif the_dist_1 # first has leaves, it is "longer"
|
94
|
+
if LONG_ON_TOP
|
95
|
+
-1
|
96
|
+
else
|
97
|
+
1
|
98
|
+
end
|
99
|
+
elsif the_dist_2 # second has leaves, second is longer
|
100
|
+
if LONG_ON_TOP
|
101
|
+
1
|
102
|
+
else
|
103
|
+
-1
|
104
|
+
end
|
105
|
+
else # neither has leaves, use dist
|
106
|
+
if d1 and d2 then # check that both dists exist
|
107
|
+
if LONG_ON_TOP
|
108
|
+
d2 <=> d1
|
109
|
+
else
|
110
|
+
d1 <=> d2
|
111
|
+
end
|
112
|
+
else # just use the names
|
113
|
+
edge1[1].name.to_s <=> edge2[1].name.to_s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
out_edges.each do |src, tgt, edge|
|
119
|
+
if parents.include?(tgt) then
|
120
|
+
;;
|
121
|
+
elsif self.out_degree(tgt) == 1 then # target is a leaf?
|
122
|
+
# STDERR.puts "what is this? |#{src}| |#{tgt}|"
|
123
|
+
result << indent + __send__(format_leaf, tgt, edge, options)
|
124
|
+
else # target is an interior node
|
125
|
+
# STDERR.puts "recurse |#{src}| |#{tgt}|"
|
126
|
+
result <<
|
127
|
+
__to_newick([ src ].concat(parents), tgt, depth + 1,
|
128
|
+
format_leaf, options) +
|
129
|
+
__send__(format_leaf, tgt, edge, options)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
indent0 + "(" + newline + result.join(',' + newline) +
|
133
|
+
(result.size > 0 ? newline : '') + indent0 + ')'
|
134
|
+
end
|
135
|
+
private :__to_newick
|
136
|
+
end
|
137
|
+
|
138
|
+
opts = Trollop.options do
|
139
|
+
banner <<-EOS
|
140
|
+
|
141
|
+
Synopsis
|
142
|
+
reorder_nodes [-o order] [-c comparison_method] -i newick.tre > newick.ordered.tre
|
143
|
+
|
144
|
+
Info
|
145
|
+
Given a newick file, reorder the nodes as directed.
|
146
|
+
|
147
|
+
Option details
|
148
|
+
--infile A newick file with a single tree
|
149
|
+
--order Order in which to display nodes: increasing or decreasing
|
150
|
+
--comparison Method to sort inner nodes: mean, median, max
|
151
|
+
|
152
|
+
Options
|
153
|
+
|
154
|
+
EOS
|
155
|
+
|
156
|
+
opt(:infile,
|
157
|
+
"input file",
|
158
|
+
type: :string)
|
159
|
+
|
160
|
+
opt(:order,
|
161
|
+
"increasing or decreasing",
|
162
|
+
type: :string,
|
163
|
+
default: "increasing")
|
164
|
+
|
165
|
+
opt(:comparison,
|
166
|
+
"mean, median, or max",
|
167
|
+
type: :string,
|
168
|
+
default: "max")
|
169
|
+
end
|
170
|
+
|
171
|
+
abort_unless %w[increasing decreasing].include?(opts[:order]),
|
172
|
+
"--order must be one of increasing or decreasing, " +
|
173
|
+
"got #{opts[:order]}.\nTry reorder_nodes --help for help."
|
174
|
+
|
175
|
+
if opts[:order] == "increasing"
|
176
|
+
LONG_ON_TOP = true
|
177
|
+
else
|
178
|
+
LONG_ON_TOP = false
|
179
|
+
end
|
180
|
+
|
181
|
+
abort_unless %w[mean median max].include?(opts[:comparison]),
|
182
|
+
"--comparison must be one of mean, median, or max, " +
|
183
|
+
"got #{opts[:comparison]}.\n" +
|
184
|
+
"Try reorder_nodes --help for help."
|
185
|
+
|
186
|
+
COMP_METHOD = opts[:comparison]
|
187
|
+
|
188
|
+
abort_unless opts[:infile],
|
189
|
+
"--infile is a required option. " +
|
190
|
+
"Try reorder_nodes --help for help."
|
191
|
+
|
192
|
+
abort_unless File.exists?(opts[:infile]),
|
193
|
+
"--infile #{opts[:infile]} does not exist.\n" +
|
194
|
+
"Try reorder_nodes --help for help."
|
195
|
+
|
196
|
+
fname = opts[:infile]
|
197
|
+
|
198
|
+
tree_str = File.open(fname, 'rt').read
|
199
|
+
|
200
|
+
# this only will work if there is only one tree
|
201
|
+
newick = Bio::Newick.new(tree_str, parser: :naive, indent: false)
|
202
|
+
|
203
|
+
tree = newick.tree
|
204
|
+
|
205
|
+
# warn tree_str
|
206
|
+
puts tree.newick
|
data/iroki.gemspec
CHANGED
data/lib/iroki/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iroki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Moore
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -128,6 +128,20 @@ dependencies:
|
|
128
128
|
- - "~>"
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '1.5'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: color
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '1.8'
|
138
|
+
type: :runtime
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '1.8'
|
131
145
|
- !ruby/object:Gem::Dependency
|
132
146
|
name: trollop
|
133
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -155,6 +169,8 @@ email:
|
|
155
169
|
executables:
|
156
170
|
- iroki
|
157
171
|
- newick_to_phyloxml
|
172
|
+
- newick_to_phyloxml_color
|
173
|
+
- reorder_nodes
|
158
174
|
extensions: []
|
159
175
|
extra_rdoc_files: []
|
160
176
|
files:
|
@@ -174,6 +190,8 @@ files:
|
|
174
190
|
- bin/setup
|
175
191
|
- exe/iroki
|
176
192
|
- exe/newick_to_phyloxml
|
193
|
+
- exe/newick_to_phyloxml_color
|
194
|
+
- exe/reorder_nodes
|
177
195
|
- iroki.gemspec
|
178
196
|
- lib/iroki.rb
|
179
197
|
- lib/iroki/color/color.rb
|