annotate utils/maf_utilities.py @ 1:717aee069681

Add bx-python dependency.
author Nate Coraor <nate@bx.psu.edu>
date Mon, 17 Nov 2014 10:15:05 -0500
parents 2126e1b833a2
children 16df616b39e5
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
1 #!/usr/bin/env python
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
2 """
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
3 Provides wrappers and utilities for working with MAF files and alignments.
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
4 """
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
5 #Dan Blankenberg
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
6 import bx.align.maf
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
7 import bx.intervals
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
8 import bx.interval_index_file
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
9 import sys, os, string, tempfile
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
10 import logging
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
11 from copy import deepcopy
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
12
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
13 assert sys.version_info[:2] >= ( 2, 4 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
14
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
15 log = logging.getLogger(__name__)
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
16
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
17
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
18 GAP_CHARS = [ '-' ]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
19 SRC_SPLIT_CHAR = '.'
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
20
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
21 def src_split( src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
22 fields = src.split( SRC_SPLIT_CHAR, 1 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
23 spec = fields.pop( 0 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
24 if fields:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
25 chrom = fields.pop( 0 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
26 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
27 chrom = spec
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
28 return spec, chrom
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
29
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
30 def src_merge( spec, chrom, contig = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
31 if None in [ spec, chrom ]:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
32 spec = chrom = spec or chrom
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
33 return bx.align.maf.src_merge( spec, chrom, contig )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
34
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
35 def get_species_in_block( block ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
36 species = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
37 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
38 spec, chrom = src_split( c.src )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
39 if spec not in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
40 species.append( spec )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
41 return species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
42
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
43 def tool_fail( msg = "Unknown Error" ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
44 print >> sys.stderr, "Fatal Error: %s" % msg
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
45 sys.exit()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
46
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
47 #an object corresponding to a reference layered alignment
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
48 class RegionAlignment( object ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
49
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
50 DNA_COMPLEMENT = string.maketrans( "ACGTacgt", "TGCAtgca" )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
51 MAX_SEQUENCE_SIZE = sys.maxint #Maximum length of sequence allowed
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
52
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
53 def __init__( self, size, species = [] ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
54 assert size <= self.MAX_SEQUENCE_SIZE, "Maximum length allowed for an individual sequence has been exceeded (%i > %i)." % ( size, self.MAX_SEQUENCE_SIZE )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
55 self.size = size
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
56 self.sequences = {}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
57 if not isinstance( species, list ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
58 species = [species]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
59 for spec in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
60 self.add_species( spec )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
61
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
62 #add a species to the alignment
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
63 def add_species( self, species ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
64 #make temporary sequence files
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
65 self.sequences[species] = tempfile.TemporaryFile()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
66 self.sequences[species].write( "-" * self.size )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
67
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
68 #returns the names for species found in alignment, skipping names as requested
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
69 def get_species_names( self, skip = [] ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
70 if not isinstance( skip, list ): skip = [skip]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
71 names = self.sequences.keys()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
72 for name in skip:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
73 try: names.remove( name )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
74 except: pass
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
75 return names
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
76
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
77 #returns the sequence for a species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
78 def get_sequence( self, species ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
79 self.sequences[species].seek( 0 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
80 return self.sequences[species].read()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
81
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
82 #returns the reverse complement of the sequence for a species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
83 def get_sequence_reverse_complement( self, species ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
84 complement = [base for base in self.get_sequence( species ).translate( self.DNA_COMPLEMENT )]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
85 complement.reverse()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
86 return "".join( complement )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
87
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
88 #sets a position for a species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
89 def set_position( self, index, species, base ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
90 if len( base ) != 1: raise Exception( "A genomic position can only have a length of 1." )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
91 return self.set_range( index, species, base )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
92 #sets a range for a species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
93 def set_range( self, index, species, bases ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
94 if index >= self.size or index < 0: raise Exception( "Your index (%i) is out of range (0 - %i)." % ( index, self.size - 1 ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
95 if len( bases ) == 0: raise Exception( "A set of genomic positions can only have a positive length." )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
96 if species not in self.sequences.keys(): self.add_species( species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
97 self.sequences[species].seek( index )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
98 self.sequences[species].write( bases )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
99
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
100 #Flush temp file of specified species, or all species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
101 def flush( self, species = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
102 if species is None:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
103 species = self.sequences.keys()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
104 elif not isinstance( species, list ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
105 species = [species]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
106 for spec in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
107 self.sequences[spec].flush()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
108
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
109 class GenomicRegionAlignment( RegionAlignment ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
110
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
111 def __init__( self, start, end, species = [] ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
112 RegionAlignment.__init__( self, end - start, species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
113 self.start = start
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
114 self.end = end
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
115
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
116 class SplicedAlignment( object ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
117
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
118 DNA_COMPLEMENT = string.maketrans( "ACGTacgt", "TGCAtgca" )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
119
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
120 def __init__( self, exon_starts, exon_ends, species = [] ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
121 if not isinstance( exon_starts, list ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
122 exon_starts = [exon_starts]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
123 if not isinstance( exon_ends, list ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
124 exon_ends = [exon_ends]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
125 assert len( exon_starts ) == len( exon_ends ), "The number of starts does not match the number of sizes."
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
126 self.exons = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
127 for i in range( len( exon_starts ) ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
128 self.exons.append( GenomicRegionAlignment( exon_starts[i], exon_ends[i], species ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
129
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
130 #returns the names for species found in alignment, skipping names as requested
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
131 def get_species_names( self, skip = [] ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
132 if not isinstance( skip, list ): skip = [skip]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
133 names = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
134 for exon in self.exons:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
135 for name in exon.get_species_names( skip = skip ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
136 if name not in names:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
137 names.append( name )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
138 return names
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
139
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
140 #returns the sequence for a species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
141 def get_sequence( self, species ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
142 sequence = tempfile.TemporaryFile()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
143 for exon in self.exons:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
144 if species in exon.get_species_names():
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
145 sequence.write( exon.get_sequence( species ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
146 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
147 sequence.write( "-" * exon.size )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
148 sequence.seek( 0 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
149 return sequence.read()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
150
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
151 #returns the reverse complement of the sequence for a species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
152 def get_sequence_reverse_complement( self, species ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
153 complement = [base for base in self.get_sequence( species ).translate( self.DNA_COMPLEMENT )]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
154 complement.reverse()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
155 return "".join( complement )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
156
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
157 #Start and end of coding region
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
158 @property
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
159 def start( self ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
160 return self.exons[0].start
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
161 @property
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
162 def end( self ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
163 return self.exons[-1].end
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
164
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
165 #Open a MAF index using a UID
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
166 def maf_index_by_uid( maf_uid, index_location_file ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
167 for line in open( index_location_file ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
168 try:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
169 #read each line, if not enough fields, go to next line
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
170 if line[0:1] == "#" : continue
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
171 fields = line.split('\t')
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
172 if maf_uid == fields[1]:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
173 try:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
174 maf_files = fields[4].replace( "\n", "" ).replace( "\r", "" ).split( "," )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
175 return bx.align.maf.MultiIndexed( maf_files, keep_open = True, parse_e_rows = False )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
176 except Exception, e:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
177 raise Exception( 'MAF UID (%s) found, but configuration appears to be malformed: %s' % ( maf_uid, e ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
178 except:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
179 pass
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
180 return None
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
181
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
182 #return ( index, temp_index_filename ) for user maf, if available, or build one and return it, return None when no tempfile is created
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
183 def open_or_build_maf_index( maf_file, index_filename, species = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
184 try:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
185 return ( bx.align.maf.Indexed( maf_file, index_filename = index_filename, keep_open = True, parse_e_rows = False ), None )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
186 except:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
187 return build_maf_index( maf_file, species = species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
188
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
189 def build_maf_index_species_chromosomes( filename, index_species = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
190 species = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
191 species_chromosomes = {}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
192 indexes = bx.interval_index_file.Indexes()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
193 blocks = 0
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
194 try:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
195 maf_reader = bx.align.maf.Reader( open( filename ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
196 while True:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
197 pos = maf_reader.file.tell()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
198 block = maf_reader.next()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
199 if block is None:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
200 break
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
201 blocks += 1
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
202 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
203 spec = c.src
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
204 chrom = None
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
205 if "." in spec:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
206 spec, chrom = spec.split( ".", 1 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
207 if spec not in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
208 species.append( spec )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
209 species_chromosomes[spec] = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
210 if chrom and chrom not in species_chromosomes[spec]:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
211 species_chromosomes[spec].append( chrom )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
212 if index_species is None or spec in index_species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
213 forward_strand_start = c.forward_strand_start
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
214 forward_strand_end = c.forward_strand_end
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
215 try:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
216 forward_strand_start = int( forward_strand_start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
217 forward_strand_end = int( forward_strand_end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
218 except ValueError:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
219 continue #start and end are not integers, can't add component to index, goto next component
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
220 #this likely only occurs when parse_e_rows is True?
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
221 #could a species exist as only e rows? should the
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
222 if forward_strand_end > forward_strand_start:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
223 #require positive length; i.e. certain lines have start = end = 0 and cannot be indexed
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
224 indexes.add( c.src, forward_strand_start, forward_strand_end, pos, max=c.src_size )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
225 except Exception, e:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
226 #most likely a bad MAF
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
227 log.debug( 'Building MAF index on %s failed: %s' % ( filename, e ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
228 return ( None, [], {}, 0 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
229 return ( indexes, species, species_chromosomes, blocks )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
230
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
231 #builds and returns ( index, index_filename ) for specified maf_file
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
232 def build_maf_index( maf_file, species = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
233 indexes, found_species, species_chromosomes, blocks = build_maf_index_species_chromosomes( maf_file, species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
234 if indexes is not None:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
235 fd, index_filename = tempfile.mkstemp()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
236 out = os.fdopen( fd, 'w' )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
237 indexes.write( out )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
238 out.close()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
239 return ( bx.align.maf.Indexed( maf_file, index_filename = index_filename, keep_open = True, parse_e_rows = False ), index_filename )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
240 return ( None, None )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
241
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
242 def component_overlaps_region( c, region ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
243 if c is None: return False
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
244 start, end = c.get_forward_strand_start(), c.get_forward_strand_end()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
245 if region.start >= end or region.end <= start:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
246 return False
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
247 return True
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
248
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
249 def chop_block_by_region( block, src, region, species = None, mincols = 0 ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
250 # This chopping method was designed to maintain consistency with how start/end padding gaps have been working in Galaxy thus far:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
251 # behavior as seen when forcing blocks to be '+' relative to src sequence (ref) and using block.slice_by_component( ref, slice_start, slice_end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
252 # whether-or-not this is the 'correct' behavior is questionable, but this will at least maintain consistency
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
253 # comments welcome
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
254 slice_start = block.text_size #max for the min()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
255 slice_end = 0 #min for the max()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
256 old_score = block.score #save old score for later use
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
257 # We no longer assume only one occurance of src per block, so we need to check them all
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
258 for c in iter_components_by_src( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
259 if component_overlaps_region( c, region ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
260 if c.text is not None:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
261 rev_strand = False
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
262 if c.strand == "-":
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
263 #We want our coord_to_col coordinates to be returned from positive stranded component
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
264 rev_strand = True
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
265 c = c.reverse_complement()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
266 start = max( region.start, c.start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
267 end = min( region.end, c.end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
268 start = c.coord_to_col( start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
269 end = c.coord_to_col( end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
270 if rev_strand:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
271 #need to orient slice coordinates to the original block direction
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
272 slice_len = end - start
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
273 end = len( c.text ) - start
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
274 start = end - slice_len
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
275 slice_start = min( start, slice_start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
276 slice_end = max( end, slice_end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
277
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
278 if slice_start < slice_end:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
279 block = block.slice( slice_start, slice_end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
280 if block.text_size > mincols:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
281 # restore old score, may not be accurate, but it is better than 0 for everything?
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
282 block.score = old_score
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
283 if species is not None:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
284 block = block.limit_to_species( species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
285 block.remove_all_gap_columns()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
286 return block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
287 return None
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
288
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
289 def orient_block_by_region( block, src, region, force_strand = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
290 #loop through components matching src,
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
291 #make sure each of these components overlap region
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
292 #cache strand for each of overlaping regions
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
293 #if force_strand / region.strand not in strand cache, reverse complement
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
294 ### we could have 2 sequences with same src, overlapping region, on different strands, this would cause no reverse_complementing
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
295 strands = [ c.strand for c in iter_components_by_src( block, src ) if component_overlaps_region( c, region ) ]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
296 if strands and ( force_strand is None and region.strand not in strands ) or ( force_strand is not None and force_strand not in strands ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
297 block = block.reverse_complement()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
298 return block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
299
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
300 def get_oriented_chopped_blocks_for_region( index, src, region, species = None, mincols = 0, force_strand = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
301 for block, idx, offset in get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols, force_strand ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
302 yield block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
303 def get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species = None, mincols = 0, force_strand = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
304 for block, idx, offset in get_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
305 yield orient_block_by_region( block, src, region, force_strand ), idx, offset
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
306
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
307 #split a block with multiple occurances of src into one block per src
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
308 def iter_blocks_split_by_src( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
309 for src_c in iter_components_by_src( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
310 new_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
311 new_block.text_size = block.text_size
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
312 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
313 if c == src_c or c.src != src:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
314 new_block.add_component( deepcopy( c ) ) #components have reference to alignment, dont want to loose reference to original alignment block in original components
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
315 yield new_block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
316
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
317 #split a block into multiple blocks with all combinations of a species appearing only once per block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
318 def iter_blocks_split_by_species( block, species = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
319 def __split_components_by_species( components_by_species, new_block ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
320 if components_by_species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
321 #more species with components to add to this block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
322 components_by_species = deepcopy( components_by_species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
323 spec_comps = components_by_species.pop( 0 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
324 for c in spec_comps:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
325 newer_block = deepcopy( new_block )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
326 newer_block.add_component( deepcopy( c ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
327 for value in __split_components_by_species( components_by_species, newer_block ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
328 yield value
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
329 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
330 #no more components to add, yield this block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
331 yield new_block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
332
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
333 #divide components by species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
334 spec_dict = {}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
335 if not species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
336 species = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
337 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
338 spec, chrom = src_split( c.src )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
339 if spec not in spec_dict:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
340 spec_dict[ spec ] = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
341 species.append( spec )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
342 spec_dict[ spec ].append( c )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
343 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
344 for spec in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
345 spec_dict[ spec ] = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
346 for c in iter_components_by_src_start( block, spec ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
347 spec_dict[ spec ].append( c )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
348
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
349 empty_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) ) #should we copy attributes?
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
350 empty_block.text_size = block.text_size
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
351 #call recursive function to split into each combo of spec/blocks
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
352 for value in __split_components_by_species( spec_dict.values(), empty_block ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
353 sort_block_components_by_block( value, block ) #restore original component order
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
354 yield value
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
355
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
356
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
357 #generator yielding only chopped and valid blocks for a specified region
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
358 def get_chopped_blocks_for_region( index, src, region, species = None, mincols = 0 ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
359 for block, idx, offset in get_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
360 yield block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
361 def get_chopped_blocks_with_index_offset_for_region( index, src, region, species = None, mincols = 0 ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
362 for block, idx, offset in index.get_as_iterator_with_index_and_offset( src, region.start, region.end ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
363 block = chop_block_by_region( block, src, region, species, mincols )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
364 if block is not None:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
365 yield block, idx, offset
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
366
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
367 #returns a filled region alignment for specified regions
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
368 def get_region_alignment( index, primary_species, chrom, start, end, strand = '+', species = None, mincols = 0, overwrite_with_gaps = True ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
369 if species is not None: alignment = RegionAlignment( end - start, species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
370 else: alignment = RegionAlignment( end - start, primary_species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
371 return fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand, species, mincols, overwrite_with_gaps )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
372
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
373 #reduces a block to only positions exisiting in the src provided
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
374 def reduce_block_by_primary_genome( block, species, chromosome, region_start ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
375 #returns ( startIndex, {species:texts}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
376 #where texts' contents are reduced to only positions existing in the primary genome
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
377 src = "%s.%s" % ( species, chromosome )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
378 ref = block.get_component_by_src( src )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
379 start_offset = ref.start - region_start
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
380 species_texts = {}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
381 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
382 species_texts[ c.src.split( '.' )[0] ] = list( c.text )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
383 #remove locations which are gaps in the primary species, starting from the downstream end
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
384 for i in range( len( species_texts[ species ] ) - 1, -1, -1 ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
385 if species_texts[ species ][i] == '-':
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
386 for text in species_texts.values():
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
387 text.pop( i )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
388 for spec, text in species_texts.items():
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
389 species_texts[spec] = ''.join( text )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
390 return ( start_offset, species_texts )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
391
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
392 #fills a region alignment
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
393 def fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand = '+', species = None, mincols = 0, overwrite_with_gaps = True ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
394 region = bx.intervals.Interval( start, end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
395 region.chrom = chrom
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
396 region.strand = strand
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
397 primary_src = "%s.%s" % ( primary_species, chrom )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
398
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
399 #Order blocks overlaping this position by score, lowest first
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
400 blocks = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
401 for block, idx, offset in index.get_as_iterator_with_index_and_offset( primary_src, start, end ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
402 score = float( block.score )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
403 for i in range( 0, len( blocks ) ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
404 if score < blocks[i][0]:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
405 blocks.insert( i, ( score, idx, offset ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
406 break
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
407 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
408 blocks.append( ( score, idx, offset ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
409
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
410 #gap_chars_tuple = tuple( GAP_CHARS )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
411 gap_chars_str = ''.join( GAP_CHARS )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
412 #Loop through ordered blocks and layer by increasing score
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
413 for block_dict in blocks:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
414 for block in iter_blocks_split_by_species( block_dict[1].get_at_offset( block_dict[2] ) ): #need to handle each occurance of sequence in block seperately
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
415 if component_overlaps_region( block.get_component_by_src( primary_src ), region ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
416 block = chop_block_by_region( block, primary_src, region, species, mincols ) #chop block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
417 block = orient_block_by_region( block, primary_src, region ) #orient block
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
418 start_offset, species_texts = reduce_block_by_primary_genome( block, primary_species, chrom, start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
419 for spec, text in species_texts.items():
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
420 #we should trim gaps from both sides, since these are not positions in this species genome (sequence)
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
421 text = text.rstrip( gap_chars_str )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
422 gap_offset = 0
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
423 while True in [ text.startswith( gap_char ) for gap_char in GAP_CHARS ]: #python2.4 doesn't accept a tuple for .startswith()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
424 #while text.startswith( gap_chars_tuple ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
425 gap_offset += 1
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
426 text = text[1:]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
427 if not text:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
428 break
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
429 if text:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
430 if overwrite_with_gaps:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
431 alignment.set_range( start_offset + gap_offset, spec, text )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
432 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
433 for i, char in enumerate( text ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
434 if char not in GAP_CHARS:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
435 alignment.set_position( start_offset + gap_offset + i, spec, char )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
436 return alignment
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
437
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
438 #returns a filled spliced region alignment for specified region with start and end lists
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
439 def get_spliced_region_alignment( index, primary_species, chrom, starts, ends, strand = '+', species = None, mincols = 0, overwrite_with_gaps = True ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
440 #create spliced alignment object
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
441 if species is not None: alignment = SplicedAlignment( starts, ends, species )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
442 else: alignment = SplicedAlignment( starts, ends, [primary_species] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
443 for exon in alignment.exons:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
444 fill_region_alignment( exon, index, primary_species, chrom, exon.start, exon.end, strand, species, mincols, overwrite_with_gaps )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
445 return alignment
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
446
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
447 #loop through string array, only return non-commented lines
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
448 def line_enumerator( lines, comment_start = '#' ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
449 i = 0
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
450 for line in lines:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
451 if not line.startswith( comment_start ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
452 i += 1
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
453 yield ( i, line )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
454
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
455 #read a GeneBed file, return list of starts, ends, raw fields
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
456 def get_starts_ends_fields_from_gene_bed( line ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
457 #Starts and ends for exons
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
458 starts = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
459 ends = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
460
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
461 fields = line.split()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
462 #Requires atleast 12 BED columns
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
463 if len(fields) < 12:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
464 raise Exception( "Not a proper 12 column BED line (%s)." % line )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
465 chrom = fields[0]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
466 tx_start = int( fields[1] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
467 tx_end = int( fields[2] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
468 name = fields[3]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
469 strand = fields[5]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
470 if strand != '-': strand='+' #Default strand is +
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
471 cds_start = int( fields[6] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
472 cds_end = int( fields[7] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
473
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
474 #Calculate and store starts and ends of coding exons
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
475 region_start, region_end = cds_start, cds_end
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
476 exon_starts = map( int, fields[11].rstrip( ',\n' ).split( ',' ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
477 exon_starts = map( ( lambda x: x + tx_start ), exon_starts )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
478 exon_ends = map( int, fields[10].rstrip( ',' ).split( ',' ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
479 exon_ends = map( ( lambda x, y: x + y ), exon_starts, exon_ends );
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
480 for start, end in zip( exon_starts, exon_ends ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
481 start = max( start, region_start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
482 end = min( end, region_end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
483 if start < end:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
484 starts.append( start )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
485 ends.append( end )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
486 return ( starts, ends, fields )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
487
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
488 def iter_components_by_src( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
489 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
490 if c.src == src:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
491 yield c
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
492
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
493 def get_components_by_src( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
494 return [ value for value in iter_components_by_src( block, src ) ]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
495
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
496 def iter_components_by_src_start( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
497 for c in block.components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
498 if c.src.startswith( src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
499 yield c
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
500
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
501 def get_components_by_src_start( block, src ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
502 return [ value for value in iter_components_by_src_start( block, src ) ]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
503
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
504 def sort_block_components_by_block( block1, block2 ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
505 #orders the components in block1 by the index of the component in block2
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
506 #block1 must be a subset of block2
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
507 #occurs in-place
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
508 return block1.components.sort( cmp = lambda x, y: block2.components.index( x ) - block2.components.index( y ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
509
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
510 def get_species_in_maf( maf_filename ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
511 species = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
512 for block in bx.align.maf.Reader( open( maf_filename ) ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
513 for spec in get_species_in_block( block ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
514 if spec not in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
515 species.append( spec )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
516 return species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
517
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
518 def parse_species_option( species ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
519 if species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
520 species = species.split( ',' )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
521 if 'None' not in species:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
522 return species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
523 return None #provided species was '', None, or had 'None' in it
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
524
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
525 def remove_temp_index_file( index_filename ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
526 try: os.unlink( index_filename )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
527 except: pass
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
528
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
529 #Below are methods to deal with FASTA files
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
530
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
531 def get_fasta_header( component, attributes = {}, suffix = None ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
532 header = ">%s(%s):%i-%i|" % ( component.src, component.strand, component.get_forward_strand_start(), component.get_forward_strand_end() )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
533 for key, value in attributes.iteritems():
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
534 header = "%s%s=%s|" % ( header, key, value )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
535 if suffix:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
536 header = "%s%s" % ( header, suffix )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
537 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
538 header = "%s%s" % ( header, src_split( component.src )[ 0 ] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
539 return header
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
540
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
541 def get_attributes_from_fasta_header( header ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
542 if not header: return {}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
543 attributes = {}
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
544 header = header.lstrip( '>' )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
545 header = header.strip()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
546 fields = header.split( '|' )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
547 try:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
548 region = fields[0]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
549 region = region.split( '(', 1 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
550 temp = region[0].split( '.', 1 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
551 attributes['species'] = temp[0]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
552 if len( temp ) == 2:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
553 attributes['chrom'] = temp[1]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
554 else:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
555 attributes['chrom'] = temp[0]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
556 region = region[1].split( ')', 1 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
557 attributes['strand'] = region[0]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
558 region = region[1].lstrip( ':' ).split( '-' )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
559 attributes['start'] = int( region[0] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
560 attributes['end'] = int( region[1] )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
561 except:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
562 #fields 0 is not a region coordinate
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
563 pass
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
564 if len( fields ) > 2:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
565 for i in xrange( 1, len( fields ) - 1 ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
566 prop = fields[i].split( '=', 1 )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
567 if len( prop ) == 2:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
568 attributes[ prop[0] ] = prop[1]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
569 if len( fields ) > 1:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
570 attributes['__suffix__'] = fields[-1]
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
571 return attributes
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
572
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
573 def iter_fasta_alignment( filename ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
574 class fastaComponent:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
575 def __init__( self, species, text = "" ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
576 self.species = species
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
577 self.text = text
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
578 def extend( self, text ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
579 self.text = self.text + text.replace( '\n', '' ).replace( '\r', '' ).strip()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
580 #yields a list of fastaComponents for a FASTA file
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
581 f = open( filename, 'rb' )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
582 components = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
583 #cur_component = None
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
584 while True:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
585 line = f.readline()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
586 if not line:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
587 if components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
588 yield components
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
589 return
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
590 line = line.strip()
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
591 if not line:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
592 if components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
593 yield components
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
594 components = []
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
595 elif line.startswith( '>' ):
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
596 attributes = get_attributes_from_fasta_header( line )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
597 components.append( fastaComponent( attributes['species'] ) )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
598 elif components:
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
599 components[-1].extend( line )
2126e1b833a2 Imported from capsule None
devteam
parents:
diff changeset
600