Source code for pylogeny.alignment
''' Object model defining a sequence alignment (DNA, RNA, protein sequences).
Handle input biological sequence alignment files for the purposes of
phylogenetic inference. Will read all types of alignment files by utilizing the
P4 python phylogenetic library. '''
# Date: Jan 24 2014
# Author: Alex Safatli
# E-mail: safatli@cs.dal.ca
# Imports
import os
import p4
import model
import tree
import base
from newick import newickParser
from executable import fasttree
from tempfile import NamedTemporaryFile as NTempFile
from shutil import copyfile
# Set up.
p4.var.warnReadNoFile = False
# Class Definitions
[docs]class alignment(object):
''' Wrap a biological sequence alignment to enable functionality
necessary for phylogenetic inference. Makes use of temporary
files; requires to be closed once no longer needed. '''
[docs] def __init__(self,inal=None):
''' Instantiate an object intended to wrap an alignment for the
purposes of running phylogenetic inference.
:param inal: An alignment file path (most formats are accepted).
'''
# Intergrate the p4 phylogenetic library.
if inal is None:
self.data = p4.Alignment()
else:
p4.read(inal) # Read the alignment file/string.
self.data = p4.var.alignments[-1]
# Augment alignment with a discrete state model.
self.model = model.DiscreteStateModel(self)
# Keep track of temporary files.
self.paths = {}
# Internals
def __getitem__(self,i):
return self.data.sequences[i]
def __str__(self):
return '\n'.join(self.toStrList())
def __len__(self):
return len(self.data)
def __iter__(self):
for i in xrange(self.getNumSeqs()): yield self[i]
def _makeFASTAFile(self):
''' PRIVATE: Make a temporary FASTA file. '''
if ('fasta' in self.paths):
return
fh = NTempFile(delete=False)
self.paths['fasta'] = fh.name
self.data.writeFasta(fh.name)
fh.close()
[docs] def close(self):
''' Forcefully delete all temporary files and clear data. '''
self.data = None
self.model = None
if len(self.paths) > 0:
for f in self.paths.values(): os.unlink(f)
self.paths = {}
# Accessors
[docs] def toStrList(self):
''' Get all sequences as a list of strings.
:return: a list of strings
'''
return [self.getSequenceString(i) for i in xrange(self.getNumSeqs())]
[docs] def getDataType(self):
''' Get the data type associated with this alignment (e.g., protein).
:return: a string indicating the data type ('protein', 'DNA')
'''
return self.data.dataType
[docs] def getStateModel(self):
''' Get the state model associated with this alignment. See model
module for more information.
:return: a :class:`.model.DiscreteStateModel` object
'''
return self.model
[docs] def getSize(self):
''' Return the size of the alignment, or how many characters
there are in each respective item in the alignment.
:return: an integer
'''
return len(self)
[docs] def getNumSeqs(self):
''' Return the number of sequences that are present in the
sequence alignment.
:return: an integer
'''
return len(self.data.sequences)
[docs] def getDim(self):
''' Return the dimensionality of the sequence alignment (how
many different types of characters).
:return: an integer
'''
return self.data.dim
[docs] def getSequenceString(self,i):
''' Acquire the ith sequence as a string.
:param i: an index in the alignment (associated with a sequence)
:type i: an integer
:return: a string associated with the sequence
'''
return self[i].sequence
[docs] def getFASTA(self):
''' Get (and create if not already) a path to a temporary
FASTA file. This will be deleted upon closure of the alignment
instance.
:return: a string associated with a path in the file system
'''
self._makeFASTAFile()
return self.paths['fasta']
[docs] def getApproxMLNewick(self):
''' Get a tree in newick format via use of FastTree that serves as
an approximation of the maximum likelihood tree for this data.
:return: a Newick or New Hampshire string
'''
ft = fasttree(
self.getFASTA(), isProtein=(self.data.dataType=='protein'))
return ft.run()
[docs] def getApproxMLTree(self):
''' Get a tree object for an approximation of the maximum likelihood
tree for this data using FastTree.
:return: a :class:`.tree.tree` object
'''
return tree.tree(self.getApproxMLNewick(),check=True)
[docs] def getTaxa(self):
''' Get a list of taxa names associated with the alignment.
:return: a list of strings
'''
return self.data.taxNames
[docs]class phylipFriendlyAlignment(alignment):
''' An alignment object that renames all comprising taxa in order
to be able to be written as a strict Phylip file. '''
[docs] def __init__(self,inal=None):
# Call superclass constructor.
super(phylipFriendlyAlignment,self).__init__(inal)
# Keep track of all taxa names.
self.namedict = {}
# Acquire a temporary file handle for Nexus with proper names.
self._makeProperNexusFile()
# Reassign all taxa names to shorter ones (for Phylip).
self._reassignNames()
def _makePhylipFile(self):
''' PRIVATE: Make a temporary Phylip file. '''
if ('phylip' in self.paths): return
fh = NTempFile(delete=False)
self.paths['phylip'] = fh.name
self.data.writePhylip(
fh.name,whitespaceSeparatesNames=False)
fh.close()
def _makeProperNexusFile(self):
''' PRIVATE: Make a temporary Nexus file. '''
if ('nexus' in self.paths): return
fh = NTempFile(delete=False)
self.paths['nexus'] = fh.name
self.data.writeNexus(
fName=fh.name)
fh.close()
def _reassignNames(self):
''' PRIVATE: Reassign all sequence names to shorter
Phylip-friendly integer-based names. '''
d = self.namedict
for item in self.data.sequences:
_ = item.name
ind = self.data.sequences.index(item)
name = 'T' + str(ind)
d[name] = _
item.name = name
self.data.taxNames[ind] = name
[docs] def getPhylip(self):
''' Get (and create if not already) a path to a temporary
Phylip file. This will be deleted upon closure of the alignment
instance.
:return: a string associated with a path in the file system
'''
self._makePhylipFile()
return self.paths['phylip']
[docs] def writeProperNexus(self,wri):
''' Write a Nexus file with proper names.
:param wri: a path to a (existent or unexistent) file to write to
:type wri: a string
'''
copyfile(self.paths['nexus'],wri)
return True
[docs] def convertOriginalNewick(self,tr):
''' Return a Newick string with (original) taxa names that are replaced
with the shortened forms as they are defined in this object.
:param tr: a Newick string
:type tr: a string
:return: a Newick string with all replaced names
'''
p = newickParser(tr).parse()
nodes = base.treeStructure(p).getAllNodes()
for node in nodes:
l = node.label
if l in self.namedict.values():
for n in self.namedict:
if self.namedict[n] == l:
node.label = n
break
return str(p) + ';'
[docs] def reinterpretNewick(self,tr):
''' Revert the replacing of taxa names with shortened names by changing
them back to their original form.
:param tr: a Newick string
:type tr: a string
:return: a Newick string with all replaced names
'''
p = newickParser(tr).parse()
nodes = base.treeStructure(p).getAllNodes()
for node in nodes:
l = node.label
if l in self.namedict: node.label = self.namedict[l]
return str(p) + ';'
[docs] def getProperName(self,n):
''' Return the actual name for an integer-based sequence name
that was reassigned at initialization.
:param n: a shortened taxon name from this object
:type n: a string
:return: a string (replaced with the original taxon name)
'''
if n in self.namedict: return self.namedict[n]
else: return None
[docs] def getTaxa(self):
''' Return current taxa names in the alignment.
:return: a list of shortened taxa names
'''
return [x for x in self.namedict]
[docs] def recreateObject(self):
''' Reintializes the object. '''
return phylipFriendlyAlignment(self.getFASTA())