Package Bio :: Package AlignIO :: Module NexusIO
[hide private]
[frames] | no frames]

Source Code for Module Bio.AlignIO.NexusIO

  1  # Copyright 2008-2010, 2012-2014, 2016 by Peter Cock.  All rights reserved. 
  2  # 
  3  # This file is part of the Biopython distribution and governed by your 
  4  # choice of the "Biopython License Agreement" or the "BSD 3-Clause License". 
  5  # Please see the LICENSE file that should have been included as part of this 
  6  # package. 
  7  """Bio.AlignIO support for the "nexus" file format. 
  8   
  9  You are expected to use this module via the Bio.AlignIO functions (or the 
 10  Bio.SeqIO functions if you want to work directly with the gapped sequences). 
 11   
 12  See also the Bio.Nexus module (which this code calls internally), 
 13  as this offers more than just accessing the alignment or its 
 14  sequences as SeqRecord objects. 
 15  """ 
 16   
 17  from __future__ import print_function 
 18   
 19  from Bio.SeqRecord import SeqRecord 
 20  from Bio.Nexus import Nexus 
 21  from Bio.Align import MultipleSeqAlignment 
 22  from .Interfaces import AlignmentWriter 
 23  from Bio import Alphabet 
 24   
 25   
 26  # You can get a couple of example files here: 
 27  # http://www.molecularevolution.org/resources/fileformats/ 
 28   
 29   
 30  # This is a generator function! 
31 -def NexusIterator(handle, seq_count=None):
32 """Return SeqRecord objects from a Nexus file. 33 34 Thus uses the Bio.Nexus module to do the hard work. 35 36 You are expected to call this function via Bio.SeqIO or Bio.AlignIO 37 (and not use it directly). 38 39 NOTE - We only expect ONE alignment matrix per Nexus file, 40 meaning this iterator will only yield one MultipleSeqAlignment. 41 """ 42 n = Nexus.Nexus(handle) 43 if not n.matrix: 44 # No alignment found 45 raise StopIteration 46 47 # Bio.Nexus deals with duplicated names by adding a '.copy' suffix. 48 # The original names and the modified names are kept in these two lists: 49 assert len(n.unaltered_taxlabels) == len(n.taxlabels) 50 51 if seq_count and seq_count != len(n.unaltered_taxlabels): 52 raise ValueError("Found %i sequences, but seq_count=%i" 53 % (len(n.unaltered_taxlabels), seq_count)) 54 55 # TODO - Can we extract any annotation too? 56 records = (SeqRecord(n.matrix[new_name], id=new_name, 57 name=old_name, description="") 58 for old_name, new_name 59 in zip(n.unaltered_taxlabels, n.taxlabels)) 60 # All done 61 yield MultipleSeqAlignment(records, n.alphabet)
62 63
64 -class NexusWriter(AlignmentWriter):
65 """Nexus alignment writer. 66 67 Note that Nexus files are only expected to hold ONE alignment 68 matrix. 69 70 You are expected to call this class via the Bio.AlignIO.write() or 71 Bio.SeqIO.write() functions. 72 """ 73
74 - def write_file(self, alignments):
75 """Use this to write an entire file containing the given alignments. 76 77 Arguments: 78 - alignments - A list or iterator returning MultipleSeqAlignment objects. 79 This should hold ONE and only one alignment. 80 81 """ 82 align_iter = iter(alignments) # Could have been a list 83 try: 84 first_alignment = next(align_iter) 85 except StopIteration: 86 first_alignment = None 87 if first_alignment is None: 88 # Nothing to write! 89 return 0 90 91 # Check there is only one alignment... 92 try: 93 second_alignment = next(align_iter) 94 except StopIteration: 95 second_alignment = None 96 if second_alignment is not None: 97 raise ValueError("We can only write one Alignment to a Nexus file.") 98 99 # Good. Actually write the single alignment, 100 self.write_alignment(first_alignment) 101 return 1 # we only support writing one alignment!
102
103 - def write_alignment(self, alignment):
104 # Creates an empty Nexus object, adds the sequences, 105 # and then gets Nexus to prepare the output. 106 if len(alignment) == 0: 107 raise ValueError("Must have at least one sequence") 108 columns = alignment.get_alignment_length() 109 if columns == 0: 110 raise ValueError("Non-empty sequences are required") 111 minimal_record = "#NEXUS\nbegin data; dimensions ntax=0 nchar=0; " \ 112 + "format datatype=%s; end;" \ 113 % self._classify_alphabet_for_nexus(alignment._alphabet) 114 n = Nexus.Nexus(minimal_record) 115 n.alphabet = alignment._alphabet 116 for record in alignment: 117 n.add_sequence(record.id, str(record.seq)) 118 # For smaller alignments, don't bother to interleave. 119 # For larger alginments, interleave to avoid very long lines 120 # in the output - something MrBayes can't handle. 121 # TODO - Default to always interleaving? 122 n.write_nexus_data(self.handle, interleave=(columns > 1000))
123
124 - def _classify_alphabet_for_nexus(self, alphabet):
125 """Return 'protein', 'dna', or 'rna' based on the alphabet (PRIVATE). 126 127 Raises an exception if this is not possible. 128 """ 129 # Get the base alphabet (underneath any Gapped or StopCodon encoding) 130 a = Alphabet._get_base_alphabet(alphabet) 131 132 if not isinstance(a, Alphabet.Alphabet): 133 raise TypeError("Invalid alphabet") 134 elif isinstance(a, Alphabet.ProteinAlphabet): 135 return "protein" 136 elif isinstance(a, Alphabet.DNAAlphabet): 137 return "dna" 138 elif isinstance(a, Alphabet.RNAAlphabet): 139 return "rna" 140 else: 141 # Must be something like NucleotideAlphabet or 142 # just the generic Alphabet (default for fasta files) 143 raise ValueError("Need a DNA, RNA or Protein alphabet")
144 145 146 if __name__ == "__main__": 147 from Bio._utils import run_doctest 148 run_doctest(verbose=0) 149