Source code for p4.mrp

# Matrix representation / parsimony.

from p4.tree import Tree
from p4.alignment import Alignment
from p4.p4exceptions import P4Error
import p4.func
from p4.nexussets import CharSet
from p4.node import Node
from p4.treepartitions import TreePartitions


"See also Alignment.mrpSlice()"


[docs]def mrp(trees, taxNames=None): """Code a list of trees with matrix representation. The input should be a list of p4 Tree objects. The argument 'taxNames' need not be supplied, but you can if you want to. This returns an alignment, with a character set for each input tree. For example, you might say:: read('myTrees.phy') a = mrp(var.trees) a.writeNexus('a.nex') """ gm = ['mrp()'] if not isinstance(trees, list): gm.append("The 'trees' arg should be a list of p4 tree objects.") raise P4Error(gm) for t in trees: if not isinstance(t, Tree): gm.append("The 'trees' arg should be a list of p4 tree objects.") raise P4Error(gm) myTaxNames = [] for t in trees: for n in t.iterLeavesNoRoot(): if n.name not in myTaxNames: myTaxNames.append(n.name) if taxNames: suppliedTaxNamesSet = set(taxNames) myTaxNamesSet = set(myTaxNames) if suppliedTaxNamesSet != myTaxNamesSet: print(suppliedTaxNamesSet) print(myTaxNamesSet) symDiff = myTaxNamesSet.symmetric_difference(suppliedTaxNamesSet) gm.append( "The taxNames list supplied does not represent the taxa in the input trees.") gm.append("The symmetric difference is:") gm.append(symDiff) raise P4Error(gm) else: taxNames = myTaxNames # make bitKey's for taxNames, in a dictionary txBkDict = {} for tNum in range(len(taxNames)): tx = taxNames[tNum] bk = 1 << tNum txBkDict[tx] = bk # Decorate trees with BitKeys, and count the number of splits. nSplits = 0 for t in trees: tNSplits = 0 for n in t.iterPostOrder(): if not n == t.root: if n.isLeaf: # order comes from taxNames, not from the tree n.br.bitKey = 1 << taxNames.index(n.name) else: nSplits += 1 tNSplits += 1 childrenNums = t.getChildrenNums(n) try: x = t.nodes[childrenNums[0]].br.bitKey for i in childrenNums[1:]: y = t.nodes[i].br.bitKey x = x | y except AttributeError: print("t.preAndPostOrderAreValid = %s" % t.preAndPostOrderAreValid) # t.draw() print("n is nodeNum %i" % n.nodeNum) print("childrenNums = %s" % childrenNums) raise AttributeError n.br.bitKey = x t.nSplits = tNSplits n = t.root assert not n.isLeaf childrenNums = t.getChildrenNums(n) x = t.nodes[childrenNums[0]].br.bitKey for i in childrenNums[1:]: y = t.nodes[i].br.bitKey x = x | y t.taxBits = x if nSplits == 0: for t in trees: t.write() gm = ["mrp(). No splits were found in the input trees."] gm.append("That does not work.") raise P4Error(gm) a = p4.func.newEmptyAlignment( dataType='standard', symbols='01', taxNames=taxNames, length=nSplits) a.setNexusSets() for s in a.sequences: s.sequence = list(s.sequence) siteNum = 0 tRange = range(len(taxNames)) for tNum in range(len(trees)): t = trees[tNum] if t.nSplits: csName = 'cs%i' % tNum cs = CharSet(a.nexusSets) cs.nChar = nSplits cs.name = csName cs.num = tNum cs.lowName = csName cs.format = 'vector' cs.start = siteNum for n in t.iterPostOrder(): if n != t.root: if not n.isLeaf: assert n.br.bitKey for tNum in tRange: tx = taxNames[tNum] bk = txBkDict[tx] s = a.sequences[tNum].sequence if bk & n.br.bitKey: s[siteNum] = '1' elif bk & t.taxBits: s[siteNum] = '0' else: s[siteNum] = '?' siteNum += 1 cs.mask = ['0'] * nSplits for cPos in range(cs.start, siteNum): cs.mask[cPos] = '1' cs.mask = ''.join(cs.mask) cs.standardize() a.nexusSets.charSets.append(cs) a.nexusSets.charSetsDict[cs.name] = cs for s in a.sequences: s.sequence = ''.join(s.sequence) return a
[docs]def reverseMrp(alignment): """Reconstruct trees from a matrix representation. This needs character sets, one for each tree. You might say:: read('a.nex') # read the matrix representation in a = var.alignments[0] # give the alignment a name a.setNexusSets() # apply var.nexusSets to the alignment tt = reverseMrp(a) # the function returns a list of tree objects for t in tt: t.write() """ a = alignment assert a.nexusSets assert a.nexusSets.charSets tRange = range(len(a.taxNames)) tt = [] for cs in a.nexusSets.charSets: # print cs.name # cs.dump() # cs.vectorize() vPos = 0 while cs.mask[vPos] == '0': vPos += 1 firstVPos = vPos firstSite = a.sequenceSlice(vPos) # print firstSite thisTNames = [] tNums = [] for tPos in tRange: if firstSite[tPos] != '?': thisTNames.append(a.taxNames[tPos]) tNums.append(tPos) # print thisTNames csMaskChar = cs.mask[vPos] while 1: if csMaskChar == '1': st = a.sequenceSlice(vPos) for tPos in tRange: if st[tPos] == '?': assert tPos not in tNums, "bad site %i, taxon %s" % ( vPos, a.taxNames[tPos]) else: assert tPos in tNums, "bad site %i, taxon %s" % ( vPos, a.taxNames[tPos]) vPos += 1 try: csMaskChar = cs.mask[vPos] except IndexError: break vPos = firstVPos csMaskChar = cs.mask[vPos] partitions = [] while 1: if csMaskChar == '1': st = a.sequenceSlice(vPos) txPos = 0 aPart = [] for tPos in tRange: if st[tPos] != '?': if st[tPos] == '1': aPart.append(txPos) txPos += 1 partitions.append(aPart) vPos += 1 try: csMaskChar = cs.mask[vPos] except IndexError: break # print partitions tp = TreePartitions() t = tp.makeTreeFromPartitions(partitions, taxNames=thisTNames) t.name = cs.name tt.append(t) return tt