Source code for p4.treefilelite
from p4.p4exceptions import P4Error
from p4.tree import Tree
from p4.nexus import Nexus
from p4.var import var
import sys
import os
import string
import io
import copy
[docs]
class TreeFileLite(object):
    """Get trees in big files without reading the lot into memory.
    P4 Tree objects are a little obese, and large tree files will
    flood your RAM.  This class addresses that problem by reading in
    the file as text, and then creating Tree objects only on demand.
    If the trees are not saved then there should not be such a problem
    with memory.
    Instantiate with a file name.  It can handle mcmc output from p4,
    Beast, and MrBayes, and phylip format.
    This can handle tree descriptions with line breaks.  However, it
    does not know about nexus-style 'commenting-out' [ie with square
    brackets, like this].  Also, it is not particularly robust with
    regard to being case-insensitive.  So while the usual way of
    reading in tree files via the read() command will handle nexus
    tree lines that start with tReE or trEe, TreeFileLite cannot, due
    to lazy programming.  So unless your file conforms to the
    expectations of TreeFileLite, it would be best to use read().
    To decrease bloat, it is not loaded by default when you start up
    p4.  To access it, you need to do::
      from p4.treefilelite import TreeFileLite
    The only method is getTree(), although you can get the tLines if
    you want.
    Eg to just get a few Tree objects::
      from p4.treefilelite import TreeFileLite 
      tfl = TreeFileLite('mcmc_trees_0.nex')
      for i in [23, 45, 67]:
          t = tfl.getTree(i)
          t.draw()
    or, to write some trees, as text (not as Tree objects), to a new
    file::
      from p4.treefilelite import TreeFileLite
      tfl = TreeFileLite('myBigFile.nex')
      f = open('mySmallerFile.nex', 'w')
      f.write(tfl.header)
      for i in range(24000,25000):
          f.write('tree %s\\n' % tfl.tLines[i])
      f.write('end;')
      f.close()
    """
    def __init__(self, fName=None, verbose=1):
        gm = ["TreeFileLite()  init"]
        self.fName = fName
        self.verbose = verbose
        self.translationHash = None
        self.tLines = []
        self.header = None
        self._readTreeFile()
        # self._readMrBayesFile()
        self.nSamples = len(self.tLines)
        if self.nSamples:
            if self.verbose >= 1:
                print("Got %i samples." % self.nSamples)
        else:
            gm.append("Got 0 tree samples.")
            raise P4Error(gm)
[docs]
    def getTree(self, treeNum):
        tLine = self.tLines[treeNum]
        if self.verbose >= 3:
            print(tLine)
        f = io.StringIO(tLine)
        t = Tree()
        if tLine.startswith("("):
            t.parseNewick(f, translationHash=self.translationHash)
            t.setPreAndPostOrder()
        else:
            t.parseNexus(f, translationHash=self.translationHash)
        return t 
    def _readTreeFile(self):
        gm = ["TreeFileLite._readTreeFile()"]
        # Read in the trees
        try:
            f = open(self.fName)
        except IOError:
            gm.append("Can't find tree file '%s'" % self.fName)
            raise P4Error(gm)
        fLines = f.readlines()
        f.close()
        # If it is not a nexus file, it must be a phylip file, so we
        # are done.
        lNum = 0
        aLine = fLines[0].strip()
        if not aLine.startswith("#"):
            self.tLines = fLines
            return
        # So assume it is nexus.  Get the 'header', which might be
        # useful.  Its everything up to the first tree line.
        headerLines = []
        lNum = 0
        aLine = fLines[0]
        aLine = aLine.lstrip()
        lowLine = aLine.lower()
        while 1:
            if lowLine.startswith("tree"):
                break
            headerLines.append(aLine)
            lNum += 1
            try:
                aLine = fLines[lNum]
                aLine = aLine.lstrip()
                lowLine = aLine.lower()
            except IndexError:
                headerLines = []  # something went wrong ...
                break
        if headerLines:
            self.header = ''.join(headerLines)
        # Get the translate command, if it exists
        translateLines = []
        lNum = 0
        aLine = fLines[0].strip()
        lowLine = aLine.lower()
        # print "a aLine: %s" % aLine
        try:
            while not lowLine.startswith("translate"):
                lNum += 1
                aLine = fLines[lNum].strip()
                lowLine = aLine.lower()
                if lowLine.startswith('tree'):  # then we have gone too far
                    lNum = 0
                    aLine = fLines[0].strip()
                    lowLine = aLine.lower()
                    break
        except IndexError:
            # no translate line, so go back to the beginning
            lNum = 0
            aLine = fLines[0].strip()
            lowLine = aLine.lower()
        # print "b lowLine: %s" % lowLine
        # If we got a translate line, then parse the translate command.
        assert lowLine
        if lowLine.startswith("translate"):
            lNum += 1
            aLine = fLines[lNum].strip()
            while not aLine.endswith(";"):
                translateLines.append(aLine)
                lNum += 1
                aLine = fLines[lNum].strip()
            translateLines.append(aLine)
            translateFlob = io.StringIO(' '.join(translateLines))
            nx = Nexus()
            self.translationHash = nx.readTranslateCommand(translateFlob)
            # print self.translationHash
        while not aLine.startswith("tree ") and not aLine.startswith("TREE "):
            lNum += 1
            aLine = fLines[lNum].strip()
        # Get the tree lines.
        self.tLines = []
        while 1:
            if aLine.startswith("tree ") or aLine.startswith("TREE "):
                tempLine = aLine
                # accommodate trees with line breaks.
                while aLine.find(";") < 0:
                    lNum += 1
                    aLine = fLines[lNum].strip()
                    tempLine += aLine
                self.tLines.append(tempLine[5:])
            lNum += 1
            aLine = fLines[lNum].strip()
            if aLine.startswith("end;") or aLine.startswith("End;") or aLine.startswith("ENDBLOCK;") or aLine.startswith('END'):
                break