ete2.coretype.tree

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
     378 -    def _iter_descendants_postorder(self):
     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