Python for Biologists

Lesson 7 of 14 · 11 min

Reading and Writing FASTA Files

FASTA is the most common plain-text format for storing DNA, RNA, and protein sequences. In this lesson you will open a FASTA file from disk, loop over its lines, separate the headers from the sequences by hand, and write a summary back out to a new file. You only need Python's built-in tools, so there is nothing to install.

The FASTA format

text
>seq1 human insulin fragment
ATGGCCCTGTGGATGCGCCTC
CTGCCCCTGCTGGCGCTGCTG
>seq2 human glucagon fragment
ATGAAAAGCATTTACTTTGTG
>seq3 short test
ACGTACGT

In FASTA, each record begins with a header line that starts with the > symbol and gives a name or description. One or more lines of sequence letters follow it, such as the DNA bases A, C, G, and T, or amino-acid letters for proteins. Save the text above as a file named sequences.fasta in the same folder as your Python script.

python
# open() connects Python to a file on disk; "r" means read-only.
# The with statement closes the file for you when the block ends.
with open("sequences.fasta", "r") as fasta_file:
    # A for loop reads the file one line at a time.
    for line in fasta_file:
        # .strip() removes the newline character and any spaces at the ends.
        clean_line = line.strip()
        print(clean_line)
>seq1 human insulin fragment
ATGGCCCTGTGGATGCGCCTC
CTGCCCCTGCTGGCGCTGCTG
>seq2 human glucagon fragment
ATGAAAAGCATTTACTTTGTG
>seq3 short test
ACGTACGT

open() takes the file name and a mode, and "r" tells Python to read the file. The with statement closes the file automatically when the indented block ends, even if something goes wrong along the way. The for loop hands you one line at a time, and .strip() trims the invisible newline at the end of each line plus any surrounding spaces.

python
# A dictionary stores each header together with its full sequence.
sequences = {}
header = None

with open("sequences.fasta", "r") as fasta_file:
    for line in fasta_file:
        clean_line = line.strip()
        if clean_line.startswith(">"):
            # Header line: drop the ">" and start a new, empty sequence.
            header = clean_line[1:]
            sequences[header] = ""
        else:
            # Sequence line: add its letters onto the current sequence.
            sequences[header] = sequences[header] + clean_line

# "w" opens a file for writing; .write() saves text exactly as given.
with open("lengths.txt", "w") as output_file:
    for header in sequences:
        length = len(sequences[header])
        line_out = header + "\t" + str(length)
        output_file.write(line_out + "\n")
        print("wrote:", line_out)

print("Done. Saved", len(sequences), "records to lengths.txt")
wrote: seq1 human insulin fragment	42
wrote: seq2 human glucagon fragment	21
wrote: seq3 short test	8
Done. Saved 3 records to lengths.txt

The startswith(">") test is True only for header lines, so those start a new entry in the dictionary, while clean_line[1:] slices off the leading > and keeps the rest as the name. Every other line is sequence, so we join it onto the stored string, which is how the two lines of seq1 combine into one 42-base sequence. Then len() counts the letters, and because .write() saves text exactly as given, we add "\n" (a newline) and "\t" (a tab) ourselves to separate rows and columns.

Warning: Opening a file with the "w" mode erases any existing file of that name before writing. Double-check the output name, and use the "a" (append) mode instead when you want to add to a file rather than replace it.

Try it yourself: Write a script that reads sequences.fasta and creates filtered.fasta containing only the records whose sequence is longer than 20 bases. For each record you keep, write the header on its own line (remember to add the > back) and the sequence on the next line, using .write() and the newline "\n".