Introduction to Sequence Alignments with Biopython | by Wyatt Sharber, PhD | Towards Data Science

Last week I started playing around with some bioinformatics tools in Python with the library Biopython. In my previous post, I introduced the field of bioinformatics and provided an example of downloading data from GenBank with Biopython’s API interface. Today, I want to move to a typical next step for analyzing DNA sequence data — the alignment process. I’ll give an introduction to sequence alignments, and then give a brief example of using Biopython to filter some data and run alignment software.
Introduction to Sequence Alignments
When working with biological sequence data, either DNA, RNA, or protein, biologists often want to be able to compare one sequence to another in order to make some inferences about the function or evolution of the sequences. Just like you wouldn’t want to use data from data tables where data was in the wrong column for analyses, in order to make robust inferences from sequence data, we need to make sure our sequence data is well organized or “aligned.” Unfortunately, sequence data does not come with nice labels, like a date, miles per gallon, or horsepower. Instead, all we have is the position number in the sequence, and that is relative to that sequence only. Luckily, many sequences are highly conserved or similar between related organisms (and all organisms are related to some degree!). If we’re fairly certain that we’ve obtained data from the same sequence from multiple organisms, we can put that data into a matrix that we call an alignment. If you’re only comparing two sequences, it’s called a pairwise alignment. If you’re comparing three or more sequences, it’s called a multiple sequence alignment (MSA).
Using the positions and the identity of each molecule in the sequence, we can infer the relative placement of each molecule in the matrix. Sometimes there will be differences in the sequence, for example, in a position where most sequences are C, we find a sequence with a G. This is referred to as a single nucleotide polymorphism (SNP). In other times, we find that a sequence is missing a molecule that is present in the rest, or a sequence has an extra molecule. The former is a deletion, while the latter is an insertion, together referred to as “indels.” When aligning sequences with indels, we must account for these extra or missing molecules by adding gaps to the remaining sequences. These small differences are usually the interesting parts of sequence data because the variation is how we can make inferences on the function or evolution of the sequence. Below are a couple of examples of MSAs with SNPS and indels.
Example 1. Multiple Sequence Alignment with a Single Nucleotide Polymorphism (SNP) in Sequence 3 at position 3.      1 2 3 4 5 6 7 8 <- Nucleotide PositionsSeq1 G T C G C A A A Seq2 G T C G C A A ASeq3 G T G G C A A AExample 2. Multiple Sequence Alignment with an insertion in Sequence 1 at position 6.     1 2 3 4 5 6 7 8Seq1 G T C G C A A A Seq2 G T C G C - A ASeq3 G T C G C - A A
In the two examples above, the sequence length is short, the number of sequences is small, and the similarity between sequences is high. Therefore, it’s really easy to visually align the sequences. However, if you had hundreds of sequences with thousands or tens of thousands of positions to align, you probably wouldn’t want to align it all by hand! Instead, there are programs that use algorithms to align sequences.
Alignments are quite interesting problems for algorithms. First, you want to minimize the differences between sequences by aligning them. For regions of the sequences that are different, the algorithm has two options: (1) add a gap to the alignment that says “this area is unique to this sequence and there is nothing like it in the other sequence,” or (2) align dissimilar sequences together, essentially saying “these are different here, but the surrounding sequence is similar enough to keep the positional structure the same.” If the algorithm’s only objective is to minimize difference, it would just keep adding gaps! Instead, most alignment algorithms have a secondary objective of minimizing the number of gaps as well, and gaps are given a penalty score when they are introduced. With two optimizations to perform across many sequences and many positions within a sequence, the computational time and memory requirements for these programs can be enormous. I’ve waited hours or even days for alignments to finish running!
Although aligning sequences is time consuming, the result is critically important in genetic and evolutionary studies. Similarities and differences between sequences can tell you about the evolutionary relationship of one organism to another, or the function of one gene compared to another. You can’t make these kinds of inferences without establishing the relationship between sequences. I’ve glossed over some of the finer details and assumptions that go into this process, but, in their essence, alignments are the data frames of the molecular biology world.
Using Biopython for Sequence Alignments
In my previous post, I downloaded sequence data from GenBank from samples of the SARS-CoV-2 virus (aka, COVID-19). I would like to compare these sequences to each other to do some evolutionary analyses, but that first requires an alignment! So, continuing where I left off, I’m going to use Biopython to use the multiple sequence alignment software, MUSCLE, to create my MSA.
Biopython, the Python library for bioinformatics, has several tools for manipulating and building sequence alignments. The Bio.AlignIO and the Bio.Align modules contain these tools. You can read and write alignment files, convert their types, and use the alignment software interface to create an alignment. Because alignment software is fairly slow, creating Python implementations of that software is not very efficient. Instead, Biopython just uses wrappers that execute the software written in other, faster languages. It’s basically the same as running those software from command line, but Biopython gives you the opportunity to build a workflow entirely within Python. Biopython provides wrappers for nine commonly used alignment softwares (at the time of this article), which you can view by running the following line: import Bio.Align.Applications; dir(Bio.Align.Applications).
Using the wrappers is very similar to using them in the command line. You instantiate an object with all of your parameter settings for the alignment, and then execute the object. Below, I show how I filtered my SARS-CoV-2 data to only full length sequences, and then use MUSCLE to align the sequences. (Note: You need to have MUSCLE installed first before this will work. You can download MUSCLE here. It will be helpful to rename the executable file to just ‘muscle.’ Then, you should make sure that muscle is in your $PATH, i.e., in the usr/local/bin directory. Just copy and paste your downloaded and renamed copy into that directory.)
from Bio import SeqIOfrom Bio.Align.Applications import MuscleCommandline#Read in unfiltered dataunfiltered = SeqIO.parse("../../data/raw/SARS-CoV-2.gbk", "genbank")#Drop data without (close to) full length sequencesfull_length_records = []for record in unfiltered:    if len(record.seq) > 29000:        full_length_records.append(record)#Write filtered data to fileSeqIO.write(full_length_records, "../../data/raw/SARS-CoV-2.fasta", "fasta")#Align sequences with MUSCLE (using parameters to make the alignment#process as fast as possible)muscle_cline = MuscleCommandline(input="SARS-CoV-2.fasta",                                  out="SARS-CoV-2_aligned.fasta",                                  diags = True,                                  maxiters = 1,                                  log="../../data/raw/align_log.txt")muscle_cline()
As I mentioned earlier, running the alignment takes quite a while. There are some services available online that will run your alignments faster than you can on your local machine. These may be useful if you’re short on time. Additionally, it looks like there are some emerging technologies for parallelizing this process that take advantage of systems like Hadoop and Spark, but I want to look into these further before I recommend one.
After the alignment is done, you can visually inspect it with one of many alignment viewers. Unfortunately, this is not a process easily done from the command line, so Biopython doesn’t try to give you that ability. Yes, you could read in the alignment with the Bio.AlignIO module and scroll through it iteratively by slicing, but that wouldn’t be very efficient. Here’s an example view of an alignment that I opened with NCBI’s Multiple Sequence Alignment Viewer, a browser-based option.
This is where I’m gonna stop for this week’s post on alignments. Next week, I’ll continue analyzing the SARS-CoV-2 data by utilizing the alignment created here. As usual, please let me know if you have questions or comments! Thanks for reading!