This is a test version of Biostars. For the public version, visit https://www.biostars.org.
FASTA File Concatenation Script Not Working

I'm trying to concatenate FASTA files from a single directory, but my script isn't working as expected. The function should read all .fasta files and return their concatenated sequences in uppercase.

My Current Code

def fasta_file(filename):
    sequence=''
    extensions=('.fasta')
    # filenames=input("Enter the path to files:")
    for filename in filenames:
        for extension in extensions:
            if filename.endswith(extension):
                with open (filename,'r') as f:
                    for filename in f:
                        filename=filename.strip()
                        if line and not line.startswith('>'):
                            sequence+=line
    return sequence.upper()

filenames=input("Enter the path to files:")
sequence=fasta_file(filename)
print(sequence)

Issues I've Found

  1. Variable name mismatch: Function parameter is filename (singular), but I'm trying to iterate over filenames (plural) inside the function
  2. Variable reuse: I'm reusing filename as a loop variable when reading the file, which overwrites the actual filename
  3. Undefined variable: I reference line but it's never defined—I think I meant to use filename
  4. Logic error: filenames is defined AFTER calling the function, but the function needs it
  5. Input handling: The input asks for "path to files" but the code treats it as if it's already a list of filenames

What I'm Trying to Do

  • Take a directory path as input
  • Find all .fasta files in that directory
  • Read sequences from all files (ignoring header lines that start with '>')
  • Concatenate all sequences
  • Return the combined sequence in uppercase

Expected Input/Output

Input: A directory path like /path/to/fasta/files/

Output: One concatenated sequence string in uppercase


Any help on fixing this would be greatly appreciated! Thanks in advance.

fasta python

4 answers

Just to mention, the following bash code does the same and is likely also faster.

cat *.fasta | grep -ve '^>' | tr '[:lower:]' '[:upper:]'

I appreciate it! Also, could you suggest some standard online resources to help me build confidence in Bash scripting?

This is good practice if you're learning to code, and fasta manipulations are more tricky than they first appear.

We would be remiss as a professional bioinformatics community if we didn't also suggest a more robust alternative to this however, so consider familiarising yourself with biopython.

It's dedicated sequence input and output methods will handle a lot of the common pitfalls.

An equivalent script would be:

import sys

from Bio import SeqIO
from Bio.Seq import Seq
from Bio.SeqRecord import SeqRecord


def concatenate(paths, name="concatenated"):
    seq = "".join(
        str(record.seq).upper()
        for path in paths
        for record in SeqIO.parse(path, "fasta")
    )
    return SeqRecord(Seq(seq), id=name, description=f"{len(paths)} files, {len(seq)} bp")


SeqIO.write(concatenate(sys.argv[1:]), sys.stdout, "fasta")

Which you would run like so: python concat_fasta.py ./*.fasta > combined.fasta

You can put in all of the extension checking logic back in if you want, but this can be handled now by just giving the extension of the file on the command line (./*.fasta etc).

It's important also to note that file extensions are just conventions and don't actually control what the file _is_. An advantage of using BioPython is that it will make sure all of the files really are formatted as fasta, irrespective of what the extension claims it to be.

Thank you! I’m actually a beginner in Biopython, but I’ll improve my skills over time.

You caught five of the six. The one you missed: extensions = ('.fasta') is a string, not a tuple - without a trailing comma the parentheses are just grouping, so looping over it gives you '.', 'f', 'a' and so on. Either write ('.fasta',) or drop the whole thing, since glob does that job:

from pathlib import Path

def concat_fasta(directory):
    chunks = []
    for path in sorted(Path(directory).glob("*.fasta")):
        with open(path) as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith(">"):
                    chunks.append(line)
    return "".join(chunks).upper()

print(concat_fasta(input("Enter the directory: ")))

Collecting into a list and joining at the end rather than sequence += line in the loop stops it going quadratic once the files get big. And if your data might use .fa or .fna, glob for those too - glob is also case sensitive, so .FASTA wouldn't match.

Thanks it helps me a lot.

Another option, if you're interested in a higher-level interface for sequence handling, is Sugar, a Python package I'm developing.

For example, the task above could be expressed as:

from sugar import read

seqs = read("*.fasta")
seqs.str.upper()
seqs.merge(keys=None)
seqs.write("concat.fasta")

The goal of Sugar is to make common sequence and annotation operations concise and to handle the underlying bioinformatics file formats for you.

You can install it with:

pip install rnajena-sugar

I'd be interested to hear what you think.

Log in to answer this question.