tripvote 1.3__py3-none-any.whl
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.
- tripVote/__init__.py +13 -0
- tripVote/placement_lib.py +323 -0
- tripVote/tripvote_lib.py +224 -0
- tripVote/utils.py +181 -0
- tripvote-1.3.data/scripts/tripVote_complete_trees.py +66 -0
- tripvote-1.3.data/scripts/tripVote_root_trees.py +87 -0
- tripvote-1.3.dist-info/METADATA +37 -0
- tripvote-1.3.dist-info/RECORD +13 -0
- tripvote-1.3.dist-info/WHEEL +5 -0
- tripvote-1.3.dist-info/licenses/COPYING +674 -0
- tripvote-1.3.dist-info/licenses/COPYING.LESSER +165 -0
- tripvote-1.3.dist-info/licenses/LICENSE +674 -0
- tripvote-1.3.dist-info/top_level.txt +1 -0
tripVote/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
#############################################################################
|
|
4
|
+
## this file is part of the TripletVoting package
|
|
5
|
+
## see LICENSE for terms and conditions of usage.
|
|
6
|
+
#############################################################################
|
|
7
|
+
|
|
8
|
+
PROGRAM_NAME = "TripletVoting"
|
|
9
|
+
PROGRAM_AUTHOR = ["Uyen Mai","Siavash Mirarab"]
|
|
10
|
+
PROGRAM_LICENSE = "GNU General Public License, version 3"
|
|
11
|
+
PROGRAM_VERSION = "1.3"
|
|
12
|
+
PROGRAM_YEAR = "2021"
|
|
13
|
+
PROGRAM_INSTITUTE = "Department of Computer Science and Engineering, University of California at San Diego"
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
from tqdist import *
|
|
2
|
+
from triproot import *
|
|
3
|
+
from tripVote.tripvote_lib import tripVote
|
|
4
|
+
from tripVote.utils import reroot_at_edge, sample_by_depth, prune_long, sample_by_brlen
|
|
5
|
+
from treeswift import *
|
|
6
|
+
from math import log2, ceil, sqrt, exp
|
|
7
|
+
from random import choices
|
|
8
|
+
|
|
9
|
+
MIN_SMPL_SIZE = 3
|
|
10
|
+
|
|
11
|
+
def __local_search__(tree_obj,refTrees,diam,nsample=1,sample_size='sqrt'):
|
|
12
|
+
# tree_obj is a TreeSwift object; refTrees are Newick strings
|
|
13
|
+
# Assume: 1. tree_obj has been rerooted at the desired center of the local search
|
|
14
|
+
# 2. all ref trees have been preprocessed to be rooted at the missing taxon and removed it out
|
|
15
|
+
subtrees = []
|
|
16
|
+
close_leaves = []
|
|
17
|
+
search_space = []
|
|
18
|
+
for node in tree_obj.traverse_preorder():
|
|
19
|
+
if node.is_root():
|
|
20
|
+
node.d = 0
|
|
21
|
+
else:
|
|
22
|
+
node.d = node.parent.d + 1
|
|
23
|
+
if node.d <= diam+1:
|
|
24
|
+
search_space.append(node.label)
|
|
25
|
+
if node.is_leaf():
|
|
26
|
+
close_leaves.append(node.label)
|
|
27
|
+
elif node.d == diam+1:
|
|
28
|
+
subtrees.append(node)
|
|
29
|
+
|
|
30
|
+
#print(diam,search_space)
|
|
31
|
+
sample_refTrees = []
|
|
32
|
+
for i in range(nsample):
|
|
33
|
+
sample = [x for x in close_leaves]
|
|
34
|
+
for u in subtrees:
|
|
35
|
+
T = tree_obj.extract_subtree(u)
|
|
36
|
+
nleaf =len(list(T.traverse_leaves())) if sample_size=='full' else ceil(sqrt(len(list(T.traverse_leaves()))))
|
|
37
|
+
s = sample_by_depth(T,nleaf,1,do_extract=False)
|
|
38
|
+
sample += s
|
|
39
|
+
|
|
40
|
+
#nleaf = ceil(sqrt(len(list(tree_obj.traverse_leaves()))))
|
|
41
|
+
#sample = sample_by_depth(tree_obj,nleaf,1,do_extract=False)
|
|
42
|
+
|
|
43
|
+
for treeStr in refTrees:
|
|
44
|
+
r_tree = read_tree_newick(treeStr)
|
|
45
|
+
sample_refTrees.append(r_tree.extract_tree_with(sample,suppress_unifurcations=True).newick())
|
|
46
|
+
|
|
47
|
+
_,placement_label,tripScore = tripVote(tree_obj.newick(),sample_refTrees,search_space=set(search_space))
|
|
48
|
+
placement_node = None
|
|
49
|
+
for node in tree_obj.traverse_preorder():
|
|
50
|
+
if node.label == placement_label:
|
|
51
|
+
placement_node = node
|
|
52
|
+
break
|
|
53
|
+
if placement_node is None:
|
|
54
|
+
placement_edge = (None,None)
|
|
55
|
+
elif placement_node.is_root():
|
|
56
|
+
u,v = placement_node.children
|
|
57
|
+
placement_edge = (v.label,u.label)
|
|
58
|
+
else:
|
|
59
|
+
placement_edge = (placement_label,placement_node.parent.label)
|
|
60
|
+
return placement_edge, tripScore
|
|
61
|
+
|
|
62
|
+
def compute_d2root(myTree,placement_edge):
|
|
63
|
+
v_lb,u_lb = placement_edge
|
|
64
|
+
tree_obj = read_tree_newick(myTree)
|
|
65
|
+
placement_node = None
|
|
66
|
+
|
|
67
|
+
C = tree_obj.root.children
|
|
68
|
+
if len(C) == 2 and ((C[0].label == v_lb and C[1].label == u_lb) or (C[1].label == v_lb and C[0].label == u_lb)):
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
for root in C:
|
|
72
|
+
for node in root.traverse_preorder():
|
|
73
|
+
if (node.label == v_lb and (u_lb is None or node.parent.label == u_lb)) or (node.label == u_lb and node.parent.label == v_lb):
|
|
74
|
+
placement_node = node
|
|
75
|
+
found = True
|
|
76
|
+
break
|
|
77
|
+
if placement_node is not None:
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
node = placement_node
|
|
81
|
+
d = 0
|
|
82
|
+
|
|
83
|
+
if node is None:
|
|
84
|
+
print("Warning: Could not find optimal placement by tripVote. Aborting ...")
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
while not node.is_root() and not node.parent.is_root():
|
|
88
|
+
d += 1
|
|
89
|
+
node = node.get_parent()
|
|
90
|
+
return d
|
|
91
|
+
|
|
92
|
+
def place_one_taxon_iter(myTree,refTrees,missing_taxon,max_depth='max',sample_size='sqrt',nsample=None):
|
|
93
|
+
# get initial guess
|
|
94
|
+
placement_label,score,d,rerooted_refTrees,_ = place_one_taxon(myTree,refTrees,missing_taxon,max_depth=max_depth,sample_size=sample_size,nsample=nsample,use_brlen=False,alpha=0)
|
|
95
|
+
print("Initial guess: " + placement_label + " " + str(score) + " " + str(d))
|
|
96
|
+
|
|
97
|
+
tree_obj = read_tree_newick(myTree)
|
|
98
|
+
diam = ceil(log2(len(list(tree_obj.traverse_leaves())))/2)
|
|
99
|
+
|
|
100
|
+
placement_edge = None
|
|
101
|
+
if placement_label == tree_obj.root.label:
|
|
102
|
+
u,v = tree_obj.root.children # assume the root has exactly 2 children
|
|
103
|
+
placement_edge = (u.label,v.label)
|
|
104
|
+
else:
|
|
105
|
+
for node in tree_obj.traverse_preorder():
|
|
106
|
+
if node.label == placement_label:
|
|
107
|
+
placement_edge = (node.label,node.parent.label)
|
|
108
|
+
break
|
|
109
|
+
if placement_edge is None:
|
|
110
|
+
print("Warning: could not find placement by tripVote. Aborting ...")
|
|
111
|
+
return placement_edge,d
|
|
112
|
+
|
|
113
|
+
# iterate
|
|
114
|
+
while diam > 0:
|
|
115
|
+
tree_obj = read_tree_newick(myTree)
|
|
116
|
+
placement_label = placement_edge[0]
|
|
117
|
+
# reroot at the previous placement site
|
|
118
|
+
new_root_node = None
|
|
119
|
+
for node in tree_obj.traverse_preorder():
|
|
120
|
+
if node.label == placement_label:
|
|
121
|
+
new_root_node = node
|
|
122
|
+
break
|
|
123
|
+
if new_root_node is None:
|
|
124
|
+
print("Warning: could not find placement by tripVote. Aborting ...")
|
|
125
|
+
return placement_edge,d
|
|
126
|
+
if not new_root_node is tree_obj.root:
|
|
127
|
+
reroot_at_edge(tree_obj,new_root_node,new_root_node.edge_length/2,root_label="ROOT")
|
|
128
|
+
# find new placement
|
|
129
|
+
placement_edge,_ = __local_search__(tree_obj,refTrees,diam,nsample=nsample,sample_size=sample_size)
|
|
130
|
+
d = compute_d2root(myTree,placement_edge)
|
|
131
|
+
print("Diameter " + str(diam) + ": " + str(placement_edge) + " " + str(d))
|
|
132
|
+
# reduce d to go to the next iteration
|
|
133
|
+
diam -= 1
|
|
134
|
+
|
|
135
|
+
return placement_edge,d
|
|
136
|
+
|
|
137
|
+
def __reroot_ref_tree__(tree_obj,missing_taxon,active_leafset):
|
|
138
|
+
# tree_obj: a treeswift object for a reference tree
|
|
139
|
+
# reroot the tree at the missing_taxon
|
|
140
|
+
new_root = None
|
|
141
|
+
for leaf in tree_obj.traverse_leaves():
|
|
142
|
+
if leaf.label == missing_taxon:
|
|
143
|
+
new_root = leaf
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
if new_root is None:
|
|
147
|
+
return False, tree_obj
|
|
148
|
+
|
|
149
|
+
reroot_at_edge(tree_obj,new_root,new_root.edge_length/2 if new_root.edge_length is not None else None)
|
|
150
|
+
C = tree_obj.root.children
|
|
151
|
+
c_star = None
|
|
152
|
+
|
|
153
|
+
for c in C:
|
|
154
|
+
if c.label == missing_taxon:
|
|
155
|
+
c_star = c
|
|
156
|
+
break
|
|
157
|
+
if c_star is None:
|
|
158
|
+
return False, tree_obj
|
|
159
|
+
|
|
160
|
+
tree_obj.root.remove_child(c_star)
|
|
161
|
+
tree_obj.suppress_unifurcations()
|
|
162
|
+
|
|
163
|
+
tree_obj = tree_obj.extract_tree_with(active_leafset,suppress_unifurcations=True)
|
|
164
|
+
|
|
165
|
+
return True, tree_obj
|
|
166
|
+
|
|
167
|
+
def __label_tree__(tree_obj):
|
|
168
|
+
i = 0
|
|
169
|
+
for node in tree_obj.traverse_preorder():
|
|
170
|
+
if not node.is_leaf():
|
|
171
|
+
node.label = 'I' + str(i)
|
|
172
|
+
i += 1
|
|
173
|
+
|
|
174
|
+
def place_one_taxon(myTree,refTrees,missing_taxon,max_depth='max',sample_size='sqrt',nsample='default',use_brlen=False,pseudo=1e-3,alpha=0):
|
|
175
|
+
# remove all trees in refTrees that do not have the missing_taxon
|
|
176
|
+
# and assume myTree is missing the missing_taxon
|
|
177
|
+
# myTree: a newick string. Assume it has unique labeling for all nodes
|
|
178
|
+
# refTrees: a list of newick strings
|
|
179
|
+
# missing_taxon: a leaf label (string)
|
|
180
|
+
# IMPORTANT: assumming all trees have unique labeling for each node
|
|
181
|
+
# max_depth: only count the triplets with depth up to max_depth.
|
|
182
|
+
# Special case: max_depth = 'max': include all triplets (default)
|
|
183
|
+
# max_depth = 'log2': ceil(log2(n)) where n is the number of leaves
|
|
184
|
+
tree_obj = read_tree_newick(myTree)
|
|
185
|
+
active_leafset = set(leaf.label for leaf in tree_obj.traverse_leaves())
|
|
186
|
+
|
|
187
|
+
rerooted_refTrees = []
|
|
188
|
+
sample_refTrees = []
|
|
189
|
+
W = []
|
|
190
|
+
i = 0
|
|
191
|
+
for rstr in refTrees:
|
|
192
|
+
tree_obj = read_tree_newick(rstr)
|
|
193
|
+
n = len(list(tree_obj.traverse_leaves()))
|
|
194
|
+
|
|
195
|
+
if max_depth == 'max':
|
|
196
|
+
D = n
|
|
197
|
+
elif max_depth == 'log2':
|
|
198
|
+
D = ceil(log2(n))
|
|
199
|
+
else:
|
|
200
|
+
D = int(max_depth)
|
|
201
|
+
|
|
202
|
+
success_flag, tree_obj = __reroot_ref_tree__(tree_obj,missing_taxon,active_leafset)
|
|
203
|
+
if success_flag:
|
|
204
|
+
rerooted_refTrees.append(tree_obj.newick())
|
|
205
|
+
|
|
206
|
+
if alpha != 0:
|
|
207
|
+
qdist = quartet_distance(myTree,tree_obj.newick())
|
|
208
|
+
w = exp(-alpha*qdist)
|
|
209
|
+
else:
|
|
210
|
+
w = 1
|
|
211
|
+
|
|
212
|
+
if D < n:
|
|
213
|
+
prune_long(tree_obj,D)
|
|
214
|
+
|
|
215
|
+
sample_trees = []
|
|
216
|
+
if (len(list(tree_obj.traverse_leaves())) >= 3):
|
|
217
|
+
if not nsample:
|
|
218
|
+
sample_trees = [tree_obj.newick()]
|
|
219
|
+
else:
|
|
220
|
+
nleaf = len(list(tree_obj.traverse_leaves()))
|
|
221
|
+
if sample_size == 'full':
|
|
222
|
+
sample_size = nleaf
|
|
223
|
+
elif sample_size == 'sqrt':
|
|
224
|
+
sample_size = ceil(sqrt(nleaf))
|
|
225
|
+
sample_size = max(MIN_SMPL_SIZE,sample_size)
|
|
226
|
+
if nsample == 'default':
|
|
227
|
+
nsample = ceil(90/sample_size)
|
|
228
|
+
if use_brlen:
|
|
229
|
+
sample_trees = sample_by_brlen(tree_obj,sample_size,nsample,pseudo=1e-3)
|
|
230
|
+
else:
|
|
231
|
+
sample_trees = sample_by_depth(tree_obj,sample_size,nsample)
|
|
232
|
+
sample_refTrees += sample_trees
|
|
233
|
+
W += [w]*len(sample_trees)
|
|
234
|
+
|
|
235
|
+
#with open("temp_refs.trees",'w') as f:
|
|
236
|
+
# f.write('\n'.join(sample_refTrees))
|
|
237
|
+
|
|
238
|
+
_,placement_label,tripScore = tripVote(myTree,sample_refTrees,W,do_indexing=False)
|
|
239
|
+
tree_obj = read_tree_newick(myTree)
|
|
240
|
+
placement_node = None
|
|
241
|
+
for node in tree_obj.traverse_preorder():
|
|
242
|
+
if node.label == placement_label:
|
|
243
|
+
placement_node = node
|
|
244
|
+
break
|
|
245
|
+
node = placement_node
|
|
246
|
+
d = 0
|
|
247
|
+
if node is None:
|
|
248
|
+
print("Warning: Could not find optimal placement by tripVote. Aborting ...")
|
|
249
|
+
return None,None
|
|
250
|
+
while not node.is_root() and not node.parent.is_root():
|
|
251
|
+
d += 1
|
|
252
|
+
node = node.get_parent()
|
|
253
|
+
|
|
254
|
+
# insert the missing_taxon onto the tree
|
|
255
|
+
new_node = Node()
|
|
256
|
+
new_node.label = missing_taxon
|
|
257
|
+
if placement_node.is_root():
|
|
258
|
+
new_root = Node()
|
|
259
|
+
new_root.label = "p_" + missing_taxon
|
|
260
|
+
new_root.add_child(placement_node)
|
|
261
|
+
new_root.add_child(new_node)
|
|
262
|
+
tree_obj.root = new_root
|
|
263
|
+
else:
|
|
264
|
+
v = placement_node
|
|
265
|
+
u = v.parent
|
|
266
|
+
u.remove_child(v)
|
|
267
|
+
w = Node()
|
|
268
|
+
w.label = "p_" + missing_taxon
|
|
269
|
+
u.add_child(w)
|
|
270
|
+
w.add_child(v)
|
|
271
|
+
w.add_child(new_node)
|
|
272
|
+
|
|
273
|
+
return placement_label, tripScore, d, rerooted_refTrees, tree_obj.newick()
|
|
274
|
+
|
|
275
|
+
def place_taxa(myTree,refTrees,missing_taxa,sample_size='sqrt',nsample='default'):
|
|
276
|
+
tree_obj = read_tree_newick(myTree)
|
|
277
|
+
__label_tree__(tree_obj)
|
|
278
|
+
updated_tree = tree_obj.newick()
|
|
279
|
+
for taxon in missing_taxa:
|
|
280
|
+
_,_,_,_,updated_tree = place_one_taxon(updated_tree,refTrees,taxon,max_depth='max',sample_size=sample_size,nsample=nsample,use_brlen=False,pseudo=1e-3,alpha=0)
|
|
281
|
+
return updated_tree
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def complete_gene_trees(myTrees,refTrees=None,sample_size='sqrt',nsample='default',placement_taxa=None):
|
|
285
|
+
# If refTrees is None, then use the other trees in myTrees as references
|
|
286
|
+
myTrees_labeled = []
|
|
287
|
+
|
|
288
|
+
for treeStr in myTrees:
|
|
289
|
+
tree_obj = read_tree_newick(treeStr)
|
|
290
|
+
__label_tree__(tree_obj)
|
|
291
|
+
myTrees_labeled.append(tree_obj.newick())
|
|
292
|
+
|
|
293
|
+
taxon_dict = {} # mapping taxon name to frequency
|
|
294
|
+
refs = refTrees if refTrees is not None else myTrees_labeled
|
|
295
|
+
|
|
296
|
+
if placement_taxa is None:
|
|
297
|
+
for tr in refs:
|
|
298
|
+
tree_obj = read_tree_newick(tr)
|
|
299
|
+
for taxon in tree_obj.traverse_leaves():
|
|
300
|
+
x = taxon.label
|
|
301
|
+
taxon_dict[x] = taxon_dict[x] + 1 if x in taxon_dict else 1
|
|
302
|
+
taxon_list = sorted(taxon_dict.items(), key=lambda item: -item[1])
|
|
303
|
+
else:
|
|
304
|
+
taxon_list = [(x,1) for x in placement_taxa]
|
|
305
|
+
|
|
306
|
+
completed_trees = []
|
|
307
|
+
for i,treeStr in enumerate(myTrees_labeled):
|
|
308
|
+
# preprocess
|
|
309
|
+
refs = refTrees if refTrees is not None else myTrees_labeled[:i] + myTrees_labeled[i+1:]
|
|
310
|
+
tree_obj = read_tree_newick(treeStr)
|
|
311
|
+
curr_leaf_set = set([leaf.label for leaf in tree_obj.traverse_leaves()])
|
|
312
|
+
|
|
313
|
+
# complete this tree
|
|
314
|
+
updated_tree = treeStr
|
|
315
|
+
for x,c in taxon_list:
|
|
316
|
+
if x not in curr_leaf_set:
|
|
317
|
+
# x is missing in this tree, now we insert it
|
|
318
|
+
print("Adding " + x + " to tree " + str(i+1)) # + ". Present in " + str(c) + " reference trees")
|
|
319
|
+
_,_,_,_,updated_tree = place_one_taxon(updated_tree,refs,x,max_depth='max',sample_size=sample_size,nsample=nsample,use_brlen=False,pseudo=1e-3,alpha=0)
|
|
320
|
+
completed_trees.append(updated_tree)
|
|
321
|
+
print("Completed tree " + str(i+1))
|
|
322
|
+
print(updated_tree)
|
|
323
|
+
return completed_trees
|
tripVote/tripvote_lib.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from treeswift import *
|
|
2
|
+
from tqdist import *
|
|
3
|
+
from triproot import *
|
|
4
|
+
from math import exp,log,log2,ceil,sqrt
|
|
5
|
+
import random
|
|
6
|
+
from tripVote.utils import sample_by_depth, prune_long, reroot_at_edge
|
|
7
|
+
|
|
8
|
+
def entropy(C):
|
|
9
|
+
# compute entropy
|
|
10
|
+
# first normalize
|
|
11
|
+
s = sum(C)
|
|
12
|
+
C_norm = [x/s for x in C]
|
|
13
|
+
return sum(-x*log(x) for x in C_norm)
|
|
14
|
+
|
|
15
|
+
def avg_all_pairs_dtrpl(trees):
|
|
16
|
+
# compute the average triplet distances of all pairs
|
|
17
|
+
# trees are a list of newick strings
|
|
18
|
+
count = 0
|
|
19
|
+
Sum = 0
|
|
20
|
+
n = len(trees)
|
|
21
|
+
for i in range(n):
|
|
22
|
+
for j in range(i):
|
|
23
|
+
count += 1
|
|
24
|
+
Sum += triplet_distance(trees[i],trees[j])
|
|
25
|
+
return Sum/count
|
|
26
|
+
|
|
27
|
+
def tripVote_crossValidate(refTrees,fraction_test=0.1,n_vote_groups=10,alphas=list(range(1,21))):
|
|
28
|
+
# inputs: the refTrees are all newick strings
|
|
29
|
+
|
|
30
|
+
# indexing
|
|
31
|
+
refTrees_idx = []
|
|
32
|
+
for treestr in refTrees:
|
|
33
|
+
tree_obj = read_tree_newick(treestr)
|
|
34
|
+
idx = 0
|
|
35
|
+
for node in tree_obj.traverse_preorder():
|
|
36
|
+
if not node.is_leaf():
|
|
37
|
+
ID = 'I' + str(idx)
|
|
38
|
+
node.label = ID
|
|
39
|
+
idx += 1
|
|
40
|
+
refTrees_idx.append(tree_obj.newick())
|
|
41
|
+
|
|
42
|
+
# shuffle
|
|
43
|
+
temp = refTrees_idx[1:]
|
|
44
|
+
random.shuffle(temp)
|
|
45
|
+
refTrees_idx = [refTrees_idx[0]] + temp
|
|
46
|
+
#random.shuffle(refTrees_idx)
|
|
47
|
+
|
|
48
|
+
# split out into 1 testing group and multiple voting groups
|
|
49
|
+
ntrees = len(refTrees_idx)
|
|
50
|
+
test_size = round(fraction_test * ntrees)
|
|
51
|
+
nv = (ntrees - test_size) // n_vote_groups
|
|
52
|
+
rv = (ntrees - test_size) % n_vote_groups
|
|
53
|
+
|
|
54
|
+
test_group = refTrees_idx[ntrees-test_size:]
|
|
55
|
+
vote_groups = []
|
|
56
|
+
j = 0
|
|
57
|
+
for i in range(rv):
|
|
58
|
+
vote_groups.append(refTrees_idx[j:j+nv+1])
|
|
59
|
+
j += nv+1
|
|
60
|
+
for i in range(n_vote_groups-rv):
|
|
61
|
+
vote_groups.append(refTrees_idx[j:j+nv])
|
|
62
|
+
j += nv
|
|
63
|
+
|
|
64
|
+
# compute weight matrices
|
|
65
|
+
Ws = []
|
|
66
|
+
for vTrees in vote_groups:
|
|
67
|
+
W = [ [0]*len(vTrees) for i in range(test_size) ]
|
|
68
|
+
for i,tT in enumerate(test_group):
|
|
69
|
+
for j,vT in enumerate(vTrees):
|
|
70
|
+
W[i][j] = quartet_distance(tT,vT)
|
|
71
|
+
Ws.append(W)
|
|
72
|
+
|
|
73
|
+
# voting
|
|
74
|
+
alpha_scoring = {alpha:[]*test_size for alpha in alphas}
|
|
75
|
+
for alpha in alphas:
|
|
76
|
+
for i,myTree in enumerate(test_group):
|
|
77
|
+
#votes = {}
|
|
78
|
+
rerooted_trees = []
|
|
79
|
+
myTree_obj = read_tree_newick(myTree)
|
|
80
|
+
root_ids = [x.label for x in myTree_obj.root.children + [myTree_obj.root]]
|
|
81
|
+
for j,vTrees in enumerate(vote_groups):
|
|
82
|
+
weights = Ws[j][i]
|
|
83
|
+
rT,best_id,_ = tripVote(myTree,vTrees,weights,alpha=alpha,do_indexing=False)
|
|
84
|
+
rerooted_trees.append(rT)
|
|
85
|
+
#if best_id in root_ids:
|
|
86
|
+
# best_id = 'I0'
|
|
87
|
+
#votes[best_id] = 1 if best_id not in votes else votes[best_id]+1
|
|
88
|
+
#e = entropy(votes.values())
|
|
89
|
+
#e = max(votes.values())
|
|
90
|
+
#alpha_scoring[alpha].append(e)
|
|
91
|
+
#alpha_scoring[alpha].append(votes)
|
|
92
|
+
S = avg_all_pairs_dtrpl(rerooted_trees)
|
|
93
|
+
alpha_scoring[alpha].append(S)
|
|
94
|
+
|
|
95
|
+
return alpha_scoring
|
|
96
|
+
|
|
97
|
+
def tripVote(myTree,refTrees,W=None,do_indexing=True,search_space=None):
|
|
98
|
+
myTree_obj = read_tree_newick(myTree)
|
|
99
|
+
if W is None:
|
|
100
|
+
W = [1.0]*len(refTrees)
|
|
101
|
+
|
|
102
|
+
# indexing
|
|
103
|
+
id2lb = {}
|
|
104
|
+
if do_indexing:
|
|
105
|
+
idx = 0
|
|
106
|
+
for node in myTree_obj.traverse_preorder():
|
|
107
|
+
if not node.is_leaf():
|
|
108
|
+
ID = 'I' + str(idx)
|
|
109
|
+
id2lb[ID] = node.label
|
|
110
|
+
node.label = ID
|
|
111
|
+
idx += 1
|
|
112
|
+
treestr = myTree_obj.newick()
|
|
113
|
+
|
|
114
|
+
# calling tripRootScore
|
|
115
|
+
id2score = {}
|
|
116
|
+
#count=1
|
|
117
|
+
for rtree,w in zip(refTrees,W):
|
|
118
|
+
mystr = tripRootScore(rtree,treestr)
|
|
119
|
+
if not mystr: # empty string returned by tripRootScore; usually because the refTree is too small after pruning to pair with the query tree
|
|
120
|
+
continue
|
|
121
|
+
for item in mystr.split(','):
|
|
122
|
+
ID,s = item.split(':')
|
|
123
|
+
score = float(s)
|
|
124
|
+
if ID not in id2score:
|
|
125
|
+
id2score[ID] = w*score
|
|
126
|
+
else:
|
|
127
|
+
id2score[ID] += w*score
|
|
128
|
+
best_id = None
|
|
129
|
+
max_score = -1
|
|
130
|
+
for ID in id2score:
|
|
131
|
+
if id2score[ID] > max_score:
|
|
132
|
+
lb = id2lb[ID] if ID in id2lb else ID
|
|
133
|
+
if (search_space is None) or (lb in search_space):
|
|
134
|
+
best_id = ID
|
|
135
|
+
max_score = id2score[ID]
|
|
136
|
+
|
|
137
|
+
#for ID in id2score:
|
|
138
|
+
# if abs(id2score[ID]-max_score) < 1e-4 and ID != best_id:
|
|
139
|
+
# break
|
|
140
|
+
|
|
141
|
+
# turn back the original label and find the rooting position
|
|
142
|
+
optimal_root = None
|
|
143
|
+
for node in myTree_obj.traverse_preorder():
|
|
144
|
+
if node.label == best_id and not node.is_root():
|
|
145
|
+
optimal_root = node
|
|
146
|
+
if not node.is_leaf() and do_indexing:
|
|
147
|
+
node.label = id2lb[node.label]
|
|
148
|
+
if best_id is None:
|
|
149
|
+
raise ValueError("Could not find an optimal root for the input tree")
|
|
150
|
+
return best_id,id2lb[best_id] if best_id in id2lb else best_id, max_score
|
|
151
|
+
|
|
152
|
+
def tripVote_root(myTree,refTrees,max_depth='max',sample_size='full',nsample=None): #,alpha=0):
|
|
153
|
+
new_refTrees = []
|
|
154
|
+
#W = []
|
|
155
|
+
for rstr in refTrees:
|
|
156
|
+
#qdist = quartet_distance(rstr,myTree)
|
|
157
|
+
#w = exp(-alpha*qdist)
|
|
158
|
+
tree_obj = read_tree_newick(rstr)
|
|
159
|
+
n = len(list(tree_obj.traverse_leaves()))
|
|
160
|
+
if max_depth == 'max':
|
|
161
|
+
d = n
|
|
162
|
+
elif max_depth == 'log2':
|
|
163
|
+
d = ceil(log2(n))
|
|
164
|
+
else:
|
|
165
|
+
d = int(max_depth)
|
|
166
|
+
# prune deep nodes
|
|
167
|
+
if d < n:
|
|
168
|
+
prune_long(tree_obj,d)
|
|
169
|
+
# sampling
|
|
170
|
+
if (len(list(tree_obj.traverse_leaves())) >= 3):
|
|
171
|
+
if nsample is None:
|
|
172
|
+
new_tree = tree_obj.newick()
|
|
173
|
+
new_refTrees.append(new_tree)
|
|
174
|
+
#W.append(w)
|
|
175
|
+
else:
|
|
176
|
+
nleaf = len(list(tree_obj.traverse_leaves()))
|
|
177
|
+
if sample_size == 'full':
|
|
178
|
+
sampled_nleaf = nleaf
|
|
179
|
+
elif sample_size == 'sqrt':
|
|
180
|
+
sampled_nleaf = ceil(sqrt(nleaf))
|
|
181
|
+
else:
|
|
182
|
+
sampled_nleaf = int(sample_size)
|
|
183
|
+
S = sample_by_depth(tree_obj,sampled_nleaf,nsample)
|
|
184
|
+
new_refTrees += S
|
|
185
|
+
#W += [w]*len(S)
|
|
186
|
+
# read and label myTree
|
|
187
|
+
tree_obj = read_tree_newick(myTree)
|
|
188
|
+
idx = 0
|
|
189
|
+
for node in tree_obj.traverse_preorder():
|
|
190
|
+
if not node.is_leaf():
|
|
191
|
+
ID = 'I' + str(idx)
|
|
192
|
+
node.label = ID
|
|
193
|
+
idx += 1
|
|
194
|
+
_,root_label,score = tripVote(tree_obj.newick(),new_refTrees,do_indexing=False) #,W=W)
|
|
195
|
+
print("TripVote score: " + str(score))
|
|
196
|
+
#tree_obj = read_tree_newick(myTree)
|
|
197
|
+
root_node = None
|
|
198
|
+
for node in tree_obj.traverse_preorder():
|
|
199
|
+
if node.label == root_label:
|
|
200
|
+
root_node = node
|
|
201
|
+
break
|
|
202
|
+
node = root_node
|
|
203
|
+
d2root = 0
|
|
204
|
+
if root_node is None:
|
|
205
|
+
raise ValueError("Could not find optimal root node in the input tree")
|
|
206
|
+
while not node.is_root() and not node.parent.is_root():
|
|
207
|
+
d2root += 1
|
|
208
|
+
node = node.get_parent()
|
|
209
|
+
|
|
210
|
+
if not root_node.is_root():
|
|
211
|
+
root_length = root_node.edge_length/2 if root_node.edge_length is not None else None
|
|
212
|
+
reroot_at_edge(tree_obj,root_node,root_length)
|
|
213
|
+
|
|
214
|
+
rerooted_tree = tree_obj.newick()
|
|
215
|
+
|
|
216
|
+
return rerooted_tree, d2root
|
|
217
|
+
|
|
218
|
+
if __name__ == "__main__":
|
|
219
|
+
infile = "test_cases/MV.trees"
|
|
220
|
+
with open(infile,'r') as fin:
|
|
221
|
+
refTrees = fin.read().split("\n")[:-1]
|
|
222
|
+
alpha_scoring = tripVote_crossValidate(refTrees,fraction_test=0.01,n_vote_groups=10,alphas=list(x for x in range(0,21)))
|
|
223
|
+
for alpha in alpha_scoring:
|
|
224
|
+
print(alpha,sum(alpha_scoring[alpha]))
|