# Filter BAM file for read pairs where one or both of the reads starts with a given sequence pattern

What's the most straightforward way to filter a BAM file for read pairs where one or both of the reads starts with a given sequence pattern? Would a tool that deals with UMIs work for this? Which one would be best?

• Are you interested in READs that start with a particular sequence or ALIGNMENTS that start with a particular sequence, because they are not the same thing for 2 reasons: 1) The sam format records sequences in genome orientation. Thus if the sequence of the read starts AAAA, but the read maps to the '-' strand the BAM seq field will not start AAAA, but end TTTT 2) Mappers may clip or otherwise only map part of a read. When they do this, they might only record part of the sequence of the read in the SEQ field. Thus a line with the CIGAR 5H90M will not contain the first 5nt of the read seq – Ian Sudbery Nov 15 '17 at 11:13

First option

My solution uses the following steps:

• use picard sortsam to sort the records on query-name (not samtools sort because the order is not the same between java and C )
• use jjs (java scripting engine) and the htsjdk library to build a bufferof reads having the same name. If any read starts with a pattern, print the whole buffer. The htsjdk should be already contained in the picard.jar.
• sort back to coordinate order using samtools.

All in one:

java -jar /path/to/picard.jar SortSam I=in.bam O=/dev/stdout  SO=queryname |\
jjs -cp /path/to/picard.jar  script.js |\
samtools sort -T TMP -o out.bam -

with script.js

var out=java.lang.System.out;
var File = Java.type("java.io.File");
var SamInputResource = Java.type("htsjdk.samtools.SamInputResource");
var SAMFileWriterFactory = Java.type("htsjdk.samtools.SAMFileWriterFactory");
var buffer=[];
for(;;)
{
var rec=null;
if(iter.hasNext()) rec=iter.next();
{
var i=0;
for(i=0;i< buffer.length;++i)
{
}
if(i!=buffer.length)
{
for(i=0;i< buffer.length ;++i)
{
}
}
if(rec==null) break;
buffer=[];
}
buffer.push(rec);
}

samWriter.close();

A second option is to use samtools view and awk to export the read names:

samtools view  input.bam | awk -F '\t' '{if($10 ~ /^AAAA/) print$1;}' | sort | uniq > names.txt

UPDATE: 3rd solution:

I've updated my tool samjdk: there is now a new option --pair to work with query-name-sorted BAM files.

The command would be:

java -jar picard.jar SortSam I=S1.bam O=/dev/stdout  SO=queryname |\
java -jar dist/samjdk.jar --samoutputformat BAM --pair \
samtools sort -T TMP -o out.bam -
• Does this account for the fact that BAM stores the sequence of a read in genome orientation, rather than read orientation? So if the sequence of a read mapped to the plus strand starts with AAAA it will be recorded in the BAM as ending with TTTT. – Ian Sudbery Nov 15 '17 at 11:08
• @ia no but it would be easy to test this: (R.getReadNegativeStrandFlag()?R.getReadString().endsWith("TTTT"):R.getReadString().startsWith("AAAA")) – Pierre Nov 15 '17 at 11:17
• I got it to work now with samjdk as explained in 3rd solution. Thanks a lot! – 719016 Nov 15 '17 at 16:38

For at least one in a pair matching the sequence, if the read names of pairs are identical, some grep magic could do the job:

1. Print the sequence at the beginning of the line and read the name in the second column
2. Find all lines starting with the sequence

And then just grep all found reads and save into the BAM file:

SEQ=<my_sequence>
samtools view unfiltered.bam | \
awk '{print $10 "\t"$1}' | \
grep "^\$SEQ" | \
cut -f 2 > list_of_matching_names
samtools view unfiltered.bam | \
grep -f list_of_matching_names | \
samtools view -b > filtered.bam

Two small notes:

1. You might want to consider also reverse complementary sequences.
2. You also might want to keep header of BAM file (you can extract it at the beginning and add it in the end).

I am one of the authors of the UMI-tools pacakge, and although you probably could hack together a way to do this with our package, its not what its designed for and the solutions above are probably better.

The best way to do this would be to filter the fastq before mapping if thats a possibility, if it has to be the BAM, then here is a python implementation similar to Pierres java solution above. This one fiddles about with strands and cigar operations a bit more.

First sort the reads by name with samtools sort -n

then

import pysam
inbam = pysam.AlignmentFile("mybam.bam")
outbam = pysam.AlignmentFile("mybam.filtered.bam", "wb", template=inbam)
seq_to_find = "AAGG"
rc_seq_to_find = "CCTT"

'''This function takes a list of reads and check if any of them
have the prespecific sequences at their start. If the sequence
is detected the whole buffer is output. The start of the read
is the end of the alignment if the read is on the '-' strand.

do_output=False

if segment.is_reverse:
# the last operation is stored in the first part of the
#last of the cigar tuples. Operation 5 is Hard-clip
output_condition = (not segment.cigartuples[-1][0] == 5) and \
segment.query_sequence.endswith(rc_seq_to_find)
else:
output_condition = (not segment.cigartuples[0][0] == 5) and \
segment.query_sequence.startswith(seq_to_find)

if output_condition:
do_output=True
break

if do_output:
outbam.write(segment)

last_name=""

# Loop over reads in the file

# if the name hasn't changed, the name is part of the
# same "pair"