Package ete2 :: Package coretype :: Module tree
[hide private]
[frames] | no frames]

Source Code for Module ete2.coretype.tree

   1  __VERSION__="ete2-2.0rev90"  
   2  # #START_LICENSE########################################################### 
   3  # 
   4  # Copyright (C) 2009 by Jaime Huerta Cepas. All rights reserved.   
   5  # email: jhcepas@gmail.com 
   6  # 
   7  # This file is part of the Environment for Tree Exploration program (ETE).  
   8  # http://ete.cgenomics.org 
   9  #   
  10  # ETE is free software: you can redistribute it and/or modify it 
  11  # under the terms of the GNU General Public License as published by 
  12  # the Free Software Foundation, either version 3 of the License, or 
  13  # (at your option) any later version. 
  14  #   
  15  # ETE is distributed in the hope that it will be useful, 
  16  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  17  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  18  # GNU General Public License for more details. 
  19  #   
  20  # You should have received a copy of the GNU General Public License 
  21  # along with ETE.  If not, see <http://www.gnu.org/licenses/>. 
  22  # 
  23  # #END_LICENSE############################################################# 
  24   
  25  import os 
  26  import random 
  27   
  28  __all__ = ["Tree", "TreeNode"] 
  29   
  30  from ete2.parser.newick import read_newick, write_newick 
  31   
  32  DEFAULT_COMPACT = False 
  33  DEFAULT_SHOWINTERNAL = False 
  34   
35 -class TreeError(Exception):
36 """Exception class designed for tree."""
37 - def __init__(self, value=''):
38 self.value = value
39 - def __str__(self):
40 return repr(self.value)
41
42 -class TreeNode(object):
43 """ TreeNode (Tree) class is used to store a tree structure. A tree 44 consists of a collection of TreeNode instances connected in a 45 hierarchical way. Trees can be loaded from the New Hampshire Newick 46 format (newick). 47 48 CONSTRUCTOR ARGUMENTS: 49 ====================== 50 51 * newick: Path to the file containing the tree or, alternatively, 52 the text string containing the same information. 53 54 RETURNS: 55 ======== 56 The TreeNode object which represents the base (root) of the 57 tree. 58 59 EXAMPLES: 60 ========= 61 t1 = Tree() # creates an empty tree 62 t2 = Tree( '(A:1,(B:1,(C:1,D:1):0.5):0.5);' ) 63 t3 = Tree( '/home/user/myNewickFile.txt' ) 64 """ 65
66 - def _get_dist(self):
67 return self._dist
68 - def _set_dist(self, value):
69 try: 70 self._dist = float(value) 71 except ValueError: 72 raise
73
74 - def _get_support(self):
75 return self._support
76 - def _set_support(self, value):
77 try: 78 self._support = float(value) 79 except ValueError: 80 raise
81
82 - def _get_up(self):
83 return self._up
84 - def _set_up(self, value):
85 if type(value) == type(self) or value is None: 86 self._up = value 87 else: 88 raise ValueError, "up: wrong type"
89
90 - def _get_children(self):
91 return self._children
92 - def _set_children(self, value):
93 if type(value) == list and \ 94 len(set([type(n)==type(self) for n in value]))<2: 95 self._children = value 96 else: 97 raise ValueError, "children:wrong type"
98 99 dist = property(fget=_get_dist, fset=_set_dist) 100 support = property(fget=_get_support, fset=_set_support) 101 up = property(fget=_get_up, fset=_set_up) 102 children = property(fget=_get_children, fset=_set_children) 103
104 - def __init__(self, newick=None, format=0):
105 self._children = [] 106 self._up = None 107 self._dist = 1.0 108 self._support = 1.0 109 110 self.features = set([]) 111 self.collapsed = False 112 # Add basic features 113 self.add_features(name="NoName") 114 self.features.update(["dist", "support"]) 115 # Initialize tree 116 if newick is not None: 117 read_newick(newick, root_node = self, format=format)
118
119 - def __and__(self, value):
120 """ This allows to execute tree&'A' to obtain the descendant node 121 A""" 122 value=str(value) 123 try: 124 first_match = self.iter_search_nodes(name=value).next() 125 return first_match 126 except StopIteration: 127 raise ValueError, "Node not found"
128
129 - def __add__(self, value):
130 """ This allows to sum two trees.""" 131 # Should a make the sum with two copies of the original trees? 132 if type(value) == self.__class__: 133 new_root = self.__class__() 134 new_root.add_child(self) 135 new_root.add_child(value) 136 return new_root 137 else: 138 raise ValueError, "Invalid node type"
139
140 - def __str__(self):
141 """ Print tree in newick format. """ 142 return self.get_ascii(compact=DEFAULT_COMPACT, \ 143 show_internal=DEFAULT_SHOWINTERNAL)
144
145 - def __contains__(self, item):
146 """ Check if item belongs to this node. The 'item' argument must 147 be a node instance or its associated name.""" 148 if isinstance(item, self.__class__): 149 return item in set(self.get_descendants()) 150 elif type(item)==str: 151 return item in set([n.name for n in self.get_descendants()])
152
153 - def __len__(self):
154 """Node len returns number of children.""" 155 return len(self.get_leaves())
156
157 - def __iter__(self):
158 """ Iterator over leaf nodes""" 159 return self.iter_leaves()
160
161 - def add_feature(self, pr_name, pr_value):
162 """ Adds or updates a node's feature. """ 163 setattr(self, pr_name, pr_value) 164 self.features.add(pr_name)
165
166 - def add_features(self, **features):
167 """ Adds or updates a node's feature. """ 168 for fname, fvalue in features.iteritems(): 169 setattr(self, fname, fvalue) 170 self.features.add(fname)
171
172 - def del_feature(self, pr_name):
173 """ Deletes permanently a node's feature. """ 174 if hasattr(self, pr_name): 175 delattr(self, pr_name) 176 self.features.remove(pr_name)
177 178 # Topology management
179 - def add_child(self, child=None, name=None, dist=None, support=None):
180 """ 181 Adds a new child to this node. If child node is not suplied 182 as an argument, a new node instance will be created. 183 184 ARGUMENTS: 185 ========== 186 187 * 'child': the node instance to be added as a child. 188 * 'name': the name that will be given to the child. 189 * 'dist': the distance from the node to the child. 190 * 'support': the support value of child partition. 191 192 RETURNS: 193 ======== 194 195 The child node instace 196 197 """ 198 if child is None: 199 child = self.__class__() 200 201 # This prevents from circular connections, but it would take too 202 # much time to check it every time a node is creted. 203 # 204 # if self in child: 205 # raise ValueError, "child is an ancestor of current node" 206 207 if name is not None: 208 try: 209 child.add_feature("name", str(name)) 210 except ValueError: 211 raise TreeError, "Node's name has to be a string" 212 213 if dist is not None: 214 try: 215 child.add_feature("dist", float(dist)) 216 except ValueError: 217 raise TreeError, "Node's dist has must be a float number" 218 219 if support is not None: 220 try: 221 child.add_feature("support", float(support)) 222 except ValueError: 223 raise TreeError, "Node's support must be a float number" 224 225 self.children.append(child) 226 child.up = self 227 return child
228
229 - def remove_child(self, child):
230 """ Removes a child from this node (parent and child 231 nodes still exit but are no longer connected). """ 232 try: 233 self.children.remove(child) 234 except ValueError, e: 235 raise TreeError, e 236 else: 237 child.up = None 238 return child
239
240 - def add_sister(self, sister=None, name=None, dist=None):
241 """ 242 Adds a sister to this node. If sister node is not supplied 243 as an argument, a new TreeNode instance will be created and 244 returned. 245 """ 246 if self.up == None: 247 raise TreeError, "A parent node is required to add a sister" 248 else: 249 return self.up.add_child(child=sister, name=name, dist=dist)
250
251 - def remove_sister(self, sister=None):
252 """ 253 Removes a node's sister node. It has the same effect as 254 node.up.remove_child(sister). 255 256 If a sister node is not supplied, the first sister will be deleted 257 and returned. 258 259 ARGUMENTS: 260 ========== 261 'sister': A node instance 262 263 RETURNS: 264 ======== 265 The removed node 266 267 """ 268 sisters = self.get_sisters() 269 if len(sisters)>0: 270 if sister==None: 271 sister = sisters.pop(0) 272 return self.up.remove_child(sister)
273
274 - def delete(self, prevent_nondicotomic=True):
275 """ 276 Deletes node from the tree structure. Notice that this 277 method makes 'disapear' the node from the tree structure. This 278 means that children from the deleted node are transferred to the 279 next available parent. 280 281 EXAMPLE: 282 ======== 283 / C 284 root-| 285 | / B 286 \--- H | 287 \ A 288 289 > root.delete(H) will produce this structure: 290 291 / C 292 | 293 root-|--B 294 | 295 \ A 296 297 """ 298 parent = self.up 299 if parent: 300 for ch in self.children: 301 parent.add_child(ch) 302 parent.remove_child(self) 303 304 # Avoids the parents with only one child 305 if prevent_nondicotomic and parent and\ 306 len(parent.children)<2: 307 parent.delete(prevent_nondicotomic=False)
308 309
310 - def detach(self):
311 """ 312 Detachs this node (and all its descendants) from its parent 313 and returns the referent to itself. 314 315 Detached node conserves all its structure of descendants, and can 316 be attached to another node through the 'add_child' function. This 317 mechanisim can be seen as a cut and paste.""" 318 319 if self.up: 320 self.up.children.remove(self) 321 self.up = None 322 return self
323
324 - def prune(self, nodes):
325 """ 326 Prunes the topology of this node in order to conserve only a 327 selected list of leaf or internal nodes. The algorithm deletes 328 nodes until getting a consistent topology with a subset of 329 nodes. Topology relationships among kept nodes is maintained. 330 331 ARGUMENTS: 332 ========== 333 * 'nodes' is a list of node names or node objects that must be kept. 334 335 EXAMPLES: 336 ========= 337 t = Tree("(((A:0.1, B:0.01):0.001, C:0.0001):1.0[&&NHX:name=I], (D:0.00001):0.000001[&&NHX:name=J]):2.0[&&NHX:name=root];") 338 node_C = t.search_nodes(name="C")[0] 339 t.prune(["A","D", node_C]) 340 print t 341 """ 342 343 to_keep = set(_translate_nodes(self, *nodes)) 344 to_detach = [] 345 for node in self.traverse("postorder"): 346 for c in node.children: 347 if c in to_keep: 348 to_keep.add(node) 349 break 350 if node not in to_keep: 351 to_detach.append(node) 352 for c in node.children: 353 to_detach.remove(c) 354 for node in to_detach: 355 node.detach() 356 for node in to_keep: 357 if len(node.children) == 1: 358 node.delete() 359 if len(self.children)==1: 360 self.children[0].delete()
361
362 - def iter_leaves(self):
363 """ Returns an iterator over the leaves under this node. """ 364 for n in self.traverse(strategy="preorder"): 365 if n.is_leaf(): 366 yield n
367
368 - def iter_leaf_names(self):
369 """ Returns an iterator over the leaf names under this node. """ 370 for n in self.iter_leaves(): 371 yield n.name
372
373 - def iter_descendants(self, strategy="preorder"):
374 """ Returns an iterator over descendant nodes. """ 375 for n in self.traverse(strategy=strategy): 376 if n != self: 377 yield n
379 """ Iterator over all desdecendant nodes. """ 380 current = self 381 end = self.up 382 visited_childs = set([]) 383 while current is not end: 384 childs = False 385 for c in current.children: 386 if c not in visited_childs: 387 childs = True 388 current = c 389 break 390 if not childs: 391 visited_childs.add(current) 392 yield current 393 current = current.up
394
395 - def _iter_descendants_preorder(self):
396 """ Iterator over all desdecendant nodes. """ 397 tovisit = [self] 398 while len(tovisit)>0: 399 current = tovisit.pop(0) 400 yield current 401 tovisit.extend(current.children)
402
403 - def traverse(self, strategy="preorder"):
404 """ 405 Returns an iterator that traverse the tree structure under this 406 node. 407 408 ARGUMENTS: 409 ========== 410 411 'strategy' defines the way in which tree will be 412 traversed. Possible values are: "preorder" (first parent and 413 then children) 'postorder' (first children and the parent). 414 415 """ 416 if strategy=="preorder": 417 return self._iter_descendants_preorder() 418 elif strategy=="postorder": 419 return self._iter_descendants_postorder()
420 - def swap_childs(self):
421 """ 422 Swaps current childs order. 423 """ 424 if len(self.children)>1: 425 self.children.reverse()
426 - def get_children(self):
427 """ Returns an independent list of node's children. """ 428 return [ch for ch in self.children]
429
430 - def get_sisters(self):
431 """ Returns an indepent list of sister nodes. """ 432 if self.up!=None: 433 return [ch for ch in self.up.children if ch!=self] 434 else: 435 return []
436
437 - def describe(self):
438 """ Prints general information about this node and its 439 connections.""" 440 if len(self.get_tree_root().children)==2: 441 rooting = "Yes" 442 elif len(self.get_tree_root().children)>2: 443 rooting = "No" 444 else: 445 rooting = "Unknown" 446 max_node, max_dis = get_farthest_leaf() 447 print "Number of nodes:\t %d" % len(self.get_descendants()) 448 print "Number of leaves:\t %d" % len(self.get_leaves()) 449 print "Rooted:", rooting 450 print "Max. lenght to root:" 451 print "The Farthest descendant node is", max_node.name,\ 452 "with a branch distance of", max_dist
453
454 - def write(self, features=None, outfile=None, format=0):
455 """ Returns the newick representation of this node 456 topology. Several arguments control the way in which extra 457 data is shown for every node: 458 459 features: a list of feature names that want to be shown 460 (when available) for every node. Extended newick format is 461 used to represent data. 462 463 'format' defines the newick standard used to encode the 464 tree. See tutorial for details. 465 466 Example: 467 t.get_newick(["species","name"], format=1) 468 """ 469 470 nw = write_newick(self, features = features, format=format) 471 if outfile is not None: 472 open(outfile, "w").write(nw) 473 return nw 474 else: 475 return nw
476
477 - def get_tree_root(self):
478 """ Returns the absolute root node of current tree structure. """ 479 root = self 480 while root.up is not None: 481 root = root.up 482 return root
483
484 - def get_common_ancestor(self, *target_nodes):
485 """ Returns the first common ancestor between this node and a given 486 list of 'target_nodes'. 487 488 EXAMPLES: 489 ========= 490 t = tree.Tree("(((A:0.1, B:0.01):0.001, C:0.0001):1.0[&&NHX:name=common], (D:0.00001):0.000001):2.0[&&NHX:name=root];") 491 A = t.get_descendants_by_name("A")[0] 492 C = t.get_descendants_by_name("C")[0] 493 common = A.get_common_ancestor(C) 494 print common.name 495 496 """ 497 498 # Convert node names into node instances 499 target_nodes = _translate_nodes(self, *target_nodes) 500 501 # If only one node is provided, use self as the second target 502 if type(target_nodes) != list: 503 target_nodes = [target_nodes, self] 504 elif len(target_nodes)==1: 505 target_nodes = tree_nodes.append(self) 506 507 start = target_nodes[-1] 508 targets = set(target_nodes) 509 nodes_bellow = set([start]+start.get_descendants()) 510 current = start 511 prev_node = start 512 while current is not None: 513 # all nodes under current (skip vissited) 514 new_nodes = [n for s in current.children for n in s.traverse() \ 515 if s is not prev_node]+[current] 516 nodes_bellow.update(new_nodes) 517 if targets.issubset(nodes_bellow): 518 break 519 else: 520 prev_node = current 521 current = current.up 522 523 return current
524
525 - def get_leaves(self):
526 """ 527 Returns the list of terminal nodes (leaves) under this node. 528 """ 529 return [n for n in self.iter_leaves()]
530
531 - def get_leaf_names(self):
532 """ 533 Returns the list of terminal node names under the current 534 node. 535 """ 536 return [ n.name for n in self.iter_leaves() ]
537
538 - def get_descendants(self, strategy="preorder"):
539 """ 540 Returns the list of all nodes (leaves and internal) under 541 this node. 542 re buil 543 See iter_descendants method. 544 """ 545 return [n for n in self.traverse(strategy="preorder") if n != self]
546
547 - def iter_search_nodes(self, **conditions):
548 for n in self.traverse(): 549 conditions_passed = 0 550 for key, value in conditions.iteritems(): 551 if hasattr(n, key) and getattr(n, key) == value: 552 conditions_passed +=1 553 if conditions_passed == len(conditions): 554 yield n
555
556 - def search_nodes(self, **conditions):
557 matching_nodes = [] 558 for n in self.iter_search_nodes(**conditions): 559 matching_nodes.append(n) 560 return matching_nodes
561
562 - def get_leaves_by_name(self,name):
563 """ Returns a list of nodes marching a given name. """ 564 return self.search_nodes(name=name, children=[])
565
566 - def is_leaf(self):
567 if self.collapsed or len(self.children)==0: 568 return True 569 else: 570 return False
571
572 - def is_root(self):
573 if self.up is None: 574 return True 575 else: 576 return False
577
578 - def collapse(self):
579 self.collapse = True
580 - def expand(self):
581 self.collapse = False
582 583 # Distance related functions
584 - def get_distance(self, target, target2=None, topology_only=False):
585 """ 586 587 Returns the distance between two nodes. If only one target is 588 specified, it returns the distance bewtween the target and the 589 current node. 590 591 ARGUMENTS: 592 ========== 593 'target': a node within the same tree structure. 594 595 'target2': a node within the same tree structure. If 596 not specified, current node is used as target2. 597 598 RETURNS: 599 ======== 600 the distance between nodes 601 602 """ 603 604 if target2 is None: 605 target2 = self 606 root = self.get_tree_root() 607 else: 608 # is target node under current node? 609 root = self 610 611 target, target2 = _translate_nodes(root, target, target2) 612 ancestor = root.get_common_ancestor(target, target2) 613 if ancestor is None: 614 raise TreeError, "Nodes are not connected" 615 616 dist = 0.0 617 for n in [target2, target]: 618 current = n 619 while current != ancestor: 620 if topology_only: 621 if current!=target: 622 dist += 1 623 else: 624 dist += current.dist 625 current = current.up 626 return dist
627
628 - def get_farthest_node(self, topology_only=False):
629 """ 630 Returns the node's farthest descendant or ancestor node, and the 631 distance to it. 632 633 ARGUMENTS: 634 ========== 635 636 * 'topology_only' [True or False]: defines whether branch node 637 distances should be discarded from analysis or not. If 638 "True", only topological distance (number of steps to get the 639 target node) will be used. 640 641 RETURNS: 642 ======== 643 A tuple = (farthest_node, dist_to_farthest_node) 644 645 """ 646 # Init fasthest node to current farthest leaf 647 farthest_node,farthest_dist = self.get_farthest_leaf(topology_only=topology_only) 648 prev = self 649 if topology_only: 650 cdist = 0 651 else: 652 cdist = prev.dist 653 current = prev.up 654 while current is not None: 655 for ch in current.children: 656 if ch != prev: 657 if not ch.is_leaf(): 658 fnode, fdist = ch.get_farthest_leaf(topology_only=topology_only) 659 else: 660 fnode = ch 661 fdist = 0 662 if topology_only: 663 fdist += 1.0 664 else: 665 fdist += ch.dist 666 if cdist+fdist > farthest_dist: 667 farthest_dist = cdist + fdist 668 farthest_node = fnode 669 prev = current 670 if topology_only: 671 cdist += 1 672 else: 673 cdist += prev.dist 674 current = prev.up 675 return farthest_node, farthest_dist
676
677 - def get_farthest_leaf(self, topology_only=False):
678 """ 679 Returns node's farthest descendant node (which is always a leaf), and the 680 distance to it. 681 682 ARGUMENTS: 683 ========== 684 685 * 'topology_only' [True or False]: defines whether branch node 686 distances should be discarded from analysis or not. If 687 "True", only topological distance (number of steps to get the 688 target node) will be used. 689 690 RETURNS: 691 ======== 692 A tuple = (farthest_node, dist_to_farthest_node) 693 694 """ 695 max_dist = 0.0 696 max_node = None 697 if self.is_leaf(): 698 return self, 0.0 699 else: 700 for ch in self.children: 701 node, d = ch.get_farthest_leaf(topology_only=topology_only) 702 if topology_only: 703 d += 1.0 704 else: 705 d += ch.dist 706 if d>=max_dist: 707 max_dist = d 708 max_node = node 709 return max_node, max_dist
710
711 - def get_midpoint_outgroup(self):
712 """ 713 Returns the node that divides the current tree into two distance-balanced 714 partitions. 715 """ 716 # Gets the farthest node to the current root 717 root = self.get_tree_root() 718 nA , r2A_dist = root.get_farthest_leaf() 719 nB , A2B_dist = nA.get_farthest_node() 720 721 outgroup = nA 722 middist = A2B_dist / 2.0 723 cdist = 0 724 current = nA 725 while current is not None: 726 cdist += current.dist 727 if cdist > (middist): # Deja de subir cuando se pasa del maximo 728 break 729 else: 730 current = current.up 731 return current
732
733 - def populate(self, size, names_library=[], reuse_names=True):
734 """ 735 Populates the partition under this node with a given number 736 of leaves. Internal nodes are added as required. 737 738 ARGUMENTS: 739 ========== 740 741 * 'size' is the number of leaf nodes to add to the current 742 tree structure. 743 """ 744 745 charset = "abcdefghijklmnopqrstuvwxyz" 746 prev_size = len(self) 747 terminal_nodes = set(self.get_leaves()) 748 silly_nodes = set([n for n in self.traverse() \ 749 if len(n)==1 and n.children!=[]]) 750 751 if self.is_leaf(): 752 size -=1 753 names_library = set(names_library) 754 while len(terminal_nodes) != size+prev_size: 755 try: 756 target = random.sample(silly_nodes, 1)[0] 757 silly_nodes.remove(target) 758 except ValueError: 759 target = random.sample(terminal_nodes, 1)[0] 760 terminal_nodes.remove(target) 761 silly_nodes.add(target) 762 if target is not self: 763 names_library.add(target.name) 764 #target.name = "NoName" 765 766 if len(names_library)>0: 767 tname = random.sample(names_library,1)[0] 768 if not reuse_names: 769 names_library.remove(tname) 770 771 else: 772 tname = ''.join(random.sample(charset,5)) 773 tdist = random.random() 774 new_node = target.add_child( name=tname, dist=tdist ) 775 terminal_nodes.add(new_node)
776 - def set_outgroup(self, outgroup):
777 """ 778 Sets a descendant node as the outgroup of a tree. This function 779 can be used to root a tree or even an internal node. 780 781 ARGUMENTS: 782 ========== 783 784 * 'outgroup' is a leaf or internal node under the current tree 785 structure. 786 """ 787 788 outgroup = _translate_nodes(self, outgroup) 789 790 if self == outgroup: 791 raise ValueError, "Cannot set myself as outgroup" 792 793 parent_outgroup = outgroup.up 794 795 # Detects (sub)tree root 796 n = outgroup 797 while n.up is not self: 798 n = n.up 799 800 # If outgroup is a child from root, but with more than one 801 # sister nodes, creates a new node to group them 802 803 self.children.remove(n) 804 if len(self.children)>1: 805 down_branch_connector = self.__class__() 806 down_branch_connector.dist = 0.0 807 down_branch_connector.support = n.support 808 for ch in self.get_children(): 809 down_branch_connector.children.append(ch) 810 ch.up = down_branch_connector 811 self.children.remove(ch) 812 else: 813 down_branch_connector = self.children[0] 814 815 # Connects down branch to myself or to outgroup 816 quien_va_ser_padre = parent_outgroup 817 if quien_va_ser_padre is not self: 818 # Parent-child swapping 819 quien_va_ser_hijo = quien_va_ser_padre.up 820 quien_fue_padre = None 821 buffered_dist = quien_va_ser_padre.dist 822 buffered_support = quien_va_ser_padre.support 823 824 while quien_va_ser_hijo is not self: 825 quien_va_ser_padre.children.append(quien_va_ser_hijo) 826 quien_va_ser_hijo.children.remove(quien_va_ser_padre) 827 828 buffered_dist2 = quien_va_ser_hijo.dist 829 buffered_support2 = quien_va_ser_hijo.support 830 quien_va_ser_hijo.dist = buffered_dist 831 quien_va_ser_hijo.support = buffered_support 832 buffered_dist = buffered_dist2 833 buffered_support = buffered_support2 834 835 quien_va_ser_padre.up = quien_fue_padre 836 quien_fue_padre = quien_va_ser_padre 837 838 quien_va_ser_padre = quien_va_ser_hijo 839 quien_va_ser_hijo = quien_va_ser_padre.up 840 841 quien_va_ser_padre.children.append(down_branch_connector) 842 down_branch_connector.up = quien_va_ser_padre 843 quien_va_ser_padre.up = quien_fue_padre 844 845 down_branch_connector.dist += buffered_dist 846 outgroup2 = parent_outgroup 847 parent_outgroup.children.remove(outgroup) 848 outgroup2.dist = 0 849 850 else: 851 outgroup2 = down_branch_connector 852 853 outgroup.up = self 854 outgroup2.up = self 855 self.children = [outgroup,outgroup2] 856 middist = (outgroup2.dist + outgroup.dist)/2 857 outgroup.dist = middist 858 outgroup2.dist = middist 859 outgroup2.support = outgroup.support 860 self.children.sort()
861
862 - def unroot(self):
863 """ Unroots this node. This function is intented to be used over 864 the absolute tree root node, but it can be also be applied to any 865 other internal node. """ 866 # if is rooted 867 if not self.is_root(): 868 print >>sys.stderr, "Warning. You are unrooting an internal node.!!" 869 if len(self.children)==2: 870 if not self.children[0].is_leaf(): 871 self.children[0].delete() 872 elif not self.children[1].is_leaf(): 873 self.children[1].delete() 874 else: 875 raise TreeError, "Cannot unroot a tree with only two leaves"
876
877 - def show(self, layout=None, \ 878 image_properties=None):
879 """ Begins an interative session to visualize this node 880 structure.""" 881 try: 882 from ete2.treeview import drawer 883 except ImportError, e: 884 print "'treeview' module could not be loaded.\n",e 885 print "\n\n" 886 print self 887 else: 888 drawer.show_tree(self,layout,image_properties)
889
890 - def render(self, file_name, layout=None, w=None, h=None, \ 891 img_properties=None, header=None):
892 """ Renders the tree structure into an image file. """ 893 try: 894 from ete2.treeview import drawer 895 except ImportError, e: 896 print "'treeview' module could not be loaded.\n",e 897 print "\n\n" 898 print self 899 print e 900 else: 901 drawer.render_tree(self, file_name, w=w, h=h, style=layout, \ 902 img_properties=img_properties, \ 903 header=header)
904
905 - def _asciiArt(self, char1='-', show_internal=True, compact=False):
906 """ 907 Returns the ASCII representation of the tree. Code taken from the 908 PyCogent GPL project. 909 """ 910 911 LEN = 5 912 PAD = ' ' * LEN 913 PA = ' ' * (LEN-1) 914 if not self.is_leaf(): 915 mids = [] 916 result = [] 917 for c in self.children: 918 if c is self.children[0]: 919 char2 = '/' 920 elif c is self.children[-1]: 921 char2 = '\\' 922 else: 923 char2 = '-' 924 (clines, mid) = c._asciiArt(char2, show_internal, compact) 925 mids.append(mid+len(result)) 926 result.extend(clines) 927 if not compact: 928 result.append('') 929 if not compact: 930 result.pop() 931 (lo, hi, end) = (mids[0], mids[-1], len(result)) 932 prefixes = [PAD] * (lo+1) + [PA+'|'] * (hi-lo-1) + [PAD] * (end-hi) 933 mid = (lo + hi) / 2 934 prefixes[mid] = char1 + '-'*(LEN-2) + prefixes[mid][-1] 935 result = [p+l for (p,l) in zip(prefixes, result)] 936 if show_internal: 937 stem = result[mid] 938 result[mid] = stem[0] + self.name + stem[len(self.name)+1:] 939 return (result, mid) 940 else: 941 return ([char1 + '-' + self.name], 0)
942
943 - def get_ascii(self, show_internal=True, compact=False):
944 """Returns a string containing an ascii drawing of the tree. 945 946 Arguments: 947 - show_internal: includes internal edge names. 948 - compact: use exactly one line per tip. 949 """ 950 (lines, mid) = self._asciiArt( 951 show_internal=show_internal, compact=compact) 952 return '\n'+'\n'.join(lines)
953 954
955 -def _translate_nodes(root, *nodes):
956 target_nodes = [] 957 for n in nodes: 958 if type(n) is str: 959 mnodes = root.search_nodes(name=n) 960 if len(mnodes) == 0: 961 raise ValueError, "Node name not found: "+str(n) 962 elif len(mnodes)>1: 963 raise ValueError, "Ambiguos node name: "+str(n) 964 else: 965 target_nodes.append(mnodes[0]) 966 elif type(n) != root.__class__: 967 raise ValueError, "Invalid target node: "+str(n) 968 else: 969 target_nodes.append(n) 970 971 if len(target_nodes) == 1: 972 return target_nodes[0] 973 else: 974 return target_nodes
975 976 ### R bindings
977 -def asETE(R_phylo_tree):
978 try: 979 import rpy2.robjects as robjects 980 R = robjects.r 981 except ImportError, e: 982 print e 983 print >>sys.stderr, "RPy >= 2.0 is required to connect" 984 return 985 986 R.library("ape") 987 return Tree( R["write.tree"](R_phylo_tree)[0])
988
989 -def asRphylo(ETE_tree):
990 try: 991 import rpy2.robjects as robjects 992 R = robjects.r 993 except ImportError, e: 994 print e 995 print >>sys.stderr, "RPy >= 2.0 is required to connect" 996 return 997 R.library("ape") 998 return R['read.tree'](text=ETE_tree.write())
999 1000 1001 # A cosmetic alias :) 1002 Tree = TreeNode 1003