Skip to content

Commit 2f9380f

Browse files
committed
Use mapping position of read1 in addition to query name as a mate pair's key in get_readpairs
This should make get_readpairs work also for multi-mapped data, as long as mates aren't re-used in multiple (multi-mapped) pairs.
1 parent 153aafa commit 2f9380f

1 file changed

Lines changed: 21 additions & 10 deletions

File tree

umi_tools/sam_methods.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ class get_readpairs:
130130
# Used in place of a real read in incomplete pairs before they are completed
131131
PendingMate = object()
132132

133+
# Read key. To allow for multi-mapping, we also use the mapping position
134+
def get_pair_key(self, read):
135+
return (read.qname, read.reference_start if not read.is_read2 == 0 else read.next_reference_start)
136+
133137
def drain_queue_forcibly(self):
134138
# Forcibly convert incomplete queued pairs into singletons.
135139
# These necessarily contains only a read1, but not read2, since we
@@ -139,15 +143,15 @@ def drain_queue_forcibly(self):
139143
if pair[1] is get_readpairs.PendingMate:
140144
pair[1] = None
141145
U.warn("inconsistent BAM file: only read1 of proper pair %s found on chromosome %s, treating as unpaired" %
142-
(pair[0].qname, pair[0].reference_name))
146+
(str(self.get_pair_key(pair[0])), pair[0].reference_name))
143147
yield pair
144148
# Forcibly convert also incomplete unqueued pairs into singletons.
145149
# These are the ones that contain only a read2 but no read1
146150
for pair in self.incomplete_pairs.values():
147151
if pair[0] is get_readpairs.PendingMate:
148152
pair[0] = None
149153
U.warn("inconsistent BAM file: only read2 of proper pair %s found on chromosome %s, treating as unpaired" %
150-
(pair[1].qname, pair[1].reference_name))
154+
(str(self.get_pair_key(pair[1])), pair[1].reference_name))
151155
yield pair
152156
self.incomplete_pairs.clear()
153157

@@ -157,35 +161,42 @@ def __call__(self, inreads):
157161
self.current_chr = None
158162

159163
for read in inreads:
160-
read_i = int(read.is_read2)
161164
read_chr = read.reference_name
165+
# Read index. 0 for read1 and 1 for read2
166+
read_i = int(read.is_read2)
167+
pair_key = self.get_pair_key(read)
162168

163169
# Output leftover incomplete read pairs if the chrosomome changes
164170
if self.current_chr is not None and self.current_chr != read_chr:
165171
for queue_entry in self.drain_queue_forcibly():
166172
yield queue_entry
167173
self.current_chr = read_chr
168174

169-
# Queue read, either as a singleton, or as part of a read pair
170-
if read.is_unmapped or read.mate_is_unmapped or not read.is_proper_pair:
175+
# Queue read, either as a singleton, or as part of a read pair.
176+
# Proper pairs should always have both mates mapped to the same chromosome,
177+
# but to avoid weird behaviour for broken BAM file, we re-check.
178+
if ((not read.is_proper_pair) or
179+
read.is_unmapped or
180+
read.mate_is_unmapped or
181+
(read.reference_id != read.next_reference_id)):
171182
# Read is not part of a proper pair. Enqueue as singleton
172183
pair = [None, None]
173184
pair[read_i] = read
174185
self.queue.append(pair)
175186
else:
176-
if read.qname in self.incomplete_pairs:
187+
if pair_key in self.incomplete_pairs:
177188
# Read is part of an already queued (incomplete) read pair. Complete it.
178-
pair = self.incomplete_pairs.pop(read.qname)
189+
pair = self.incomplete_pairs.pop(pair_key)
179190
if pair[read_i] is not get_readpairs.PendingMate:
180-
U.warn("inconsistent BAM file: both mates %s flagged to be read%d" %
181-
(read.qname, read_i+1))
191+
U.warn("inconsistent BAM file: both mates of %s flagged to be read%d" %
192+
(str(pair_key), read_i+1))
182193
read_i = 1 - read_i
183194
pair[read_i] = read
184195
else:
185196
# Read is part of a new read pair. Create incomplete pair
186197
pair = [get_readpairs.PendingMate, get_readpairs.PendingMate]
187198
pair[read_i] = read
188-
self.incomplete_pairs[read.qname] = pair
199+
self.incomplete_pairs[pair_key] = pair
189200
# Enqueue pair in the correct place for its 1st read
190201
if read_i == 0:
191202
self.queue.append(pair)

0 commit comments

Comments
 (0)