春江暮客

春江暮客的个人学习分享网站

Protein Sequence Identity and Coverage with Biopython

2026-10-10 Technology
Protein Sequence Identity and Coverage with Biopython

A protein alignment can report 100% identity while matching less than half of the longer sequence. That can happen when a short query matches a region inside a larger protein. Without the alignment length, denominator, and coverage of both sequences, the percentage is easy to misread.

This tutorial uses Biopython to align two FASTA records and report exact residue matches, two identity definitions, and coverage for both proteins. The examples are synthetic and need no GPU or sequence database. It follows the FASTA audit workflow and helps make the similarity checks behind grouped protein dataset splits explicit.

1 Choose the alignment question

Use local alignment when you want the highest-scoring matching region. Use global alignment when you want an alignment spanning both complete inputs, with gaps allowed. These modes optimize a scoring function; neither is designed to maximize the identity percentage you report afterward. The Biopython alignment tutorial explains both modes and protein substitution matrices.

Here, both modes use BLOSUM62, a gap opening score of -10, and a gap extension score of -0.5. The settings apply to internal and terminal gaps. Under this affine convention, a gap of length k contributes -10 - 0.5 * (k - 1). These are explicit example settings, not a claim that one scoring policy suits every protein comparison.

Before comparing files from different tools, agree on the denominators:

Report field Calculation Interpretation
identity_paired_percent 100 × exact matches / residue-to-residue columns Excludes every column containing a gap.
identity_columns_percent 100 × exact matches / all alignment columns Includes gap columns in the denominator.
query_paired_coverage_percent 100 × paired residues / full query length Fraction of query residues opposite a target residue.
target_paired_coverage_percent 100 × paired residues / full target length Fraction of target residues opposite a query residue.

The two coverage fields deliberately count paired residues. They are not alignment-span coverage. A sequence insertion inside the alignment span is excluded from paired coverage if it sits opposite gaps. The script also reports query_span and target_span using zero-based, half-open coordinates so you can inspect that difference.

2 Install Biopython and download the files

Download compare_proteins.py, motif.fasta, and insertion.fasta into a new directory. Use Python 3.10 or newer. The example was tested with Python 3.14.7 and Biopython 1.88.

python3 -m venv .venv
source .venv/bin/activate
python -m pip install "biopython==1.88"
python compare_proteins.py --help

The input must contain exactly two unaligned FASTA records: query first, target second. IDs must be nonempty and distinct. This example accepts only the 20 standard amino acid letters, converts lowercase to uppercase, and rejects ambiguous symbols, stop symbols, and input gaps. It accepts a UTF-8 byte order mark. Normal FASTA sequence whitespace is handled by the parser; for a broader input audit, use the earlier FASTA tutorial.

3 Run the local motif example

The first fixture contains an eight-residue query inside an eighteen-residue target:

>query synthetic short sequence
ACDEFGHI
>target synthetic sequence with unmatched flanks
LLLLLACDEFGHILLLLL
python compare_proteins.py motif.fasta --mode local --show > motif-local.json
python -m json.tool motif-local.json

The script writes JSON to standard output and, with --show, the readable alignment to standard error. The command therefore saves only JSON in motif-local.json; use a new output filename to preserve an earlier report. The displayed alignment is:

target            5 ACDEFGHI 13
                  0 ||||||||  8
query             0 ACDEFGHI  8
Field Local result
Exact matches and paired residues 8 and 8
Alignment columns 8
Both identity percentages 100.00%
Query paired coverage 100.00%
Target paired coverage 44.44%
Query span [0, 8]
Target span [5, 13]

The target has ten residues outside the selected local alignment. The 100% identity describes the eight paired positions. It does not describe the whole target, establish a shared function, or provide a significance estimate. This script reports a raw alignment score, not a BLAST bit score or E-value.

4 Compare global alignment and an insertion

python compare_proteins.py motif.fasta --mode global --show > motif-global.json
python compare_proteins.py insertion.fasta --mode global --show > insertion-global.json

The second fixture inserts four alanines into a twenty-residue synthetic sequence:

>query synthetic reference
ACDEFGHIKLMNPQRSTVWY
>target synthetic sequence with a four-residue insertion
ACDEFGHIAAAAKLMNPQRSTVWY
Measurement Motif local Motif global Insertion global
Exact matches 8 8 20
Paired residues 8 8 20
Alignment columns 8 18 24
Identity among paired residues 100.00% 100.00% 100.00%
Identity over alignment columns 100.00% 44.44% 83.33%
Query paired coverage 100.00% 100.00% 100.00%
Target paired coverage 44.44% 44.44% 83.33%

Global alignment includes the motif target’s flanks as gap columns. It therefore changes the column-based identity while leaving paired-residue identity at 100%. In the insertion example, the target span is [0, 24], but only twenty target residues face query residues. Its span covers the entire target while its paired coverage is 83.33%.

A conservative substitution can receive a positive BLOSUM62 score and still be a mismatch for exact identity. The score helps choose the alignment; the script counts identity by comparing the aligned letters exactly.

5 Use the complete comparison script

The following code matches the downloadable file:

"""Compare exactly two protein FASTA records with explicit identity denominators."""
import argparse
from itertools import islice
import json
from pathlib import Path
import sys

import Bio
from Bio import SeqIO
from Bio.Align import PairwiseAligner, substitution_matrices

ALPHABET = set("ACDEFGHIKLMNPQRSTVWY")


def read_pair(path, max_length):
    if max_length < 1:
        raise ValueError("max length must be positive")
    with Path(path).open(encoding="utf-8-sig") as handle:
        if handle.read(1) != ">":
            raise ValueError("FASTA must begin with a header")
        handle.seek(0)
        records = list(islice(SeqIO.parse(handle, "fasta"), 3))
    if len(records) != 2:
        raise ValueError("supply exactly two FASTA records: query, then target")
    if not all(record.id for record in records) or records[0].id == records[1].id:
        raise ValueError("record IDs must be nonempty and distinct")
    sequences = [str(record.seq).upper() for record in records]
    for record, sequence in zip(records, sequences):
        if not sequence or len(sequence) > max_length:
            raise ValueError(f"{record.id}: sequence length must be 1..{max_length}")
        invalid = set(sequence) - ALPHABET
        if invalid:
            raise ValueError(f"{record.id}: unsupported symbols {''.join(sorted(invalid))!r}")
    return records, sequences


def compare(path, mode="local", max_length=5000):
    records, (query, target) = read_pair(path, max_length)
    aligner = PairwiseAligner(mode=mode)
    aligner.substitution_matrix = substitution_matrices.load("BLOSUM62")
    aligner.open_gap_score = -10.0
    aligner.extend_gap_score = -0.5
    alignment = next(iter(aligner.align(target, query)), None)
    if alignment is None:
        raise ValueError("no positive-scoring local alignment")
    paired = matches = 0
    for (t0, t1), (q0, q1) in zip(*alignment.aligned):
        left, right = target[t0:t1], query[q0:q1]
        paired += len(left)
        matches += sum(a == b for a, b in zip(left, right))
    if paired == 0:
        raise ValueError("alignment has no residue-to-residue columns")
    columns = int(alignment.length)
    result = {
        "query_id": records[0].id, "target_id": records[1].id,
        "query_length": len(query), "target_length": len(target),
        "mode": mode, "matrix": "BLOSUM62", "gap_open": -10.0, "gap_extend": -0.5,
        "biopython": Bio.__version__, "score": float(alignment.score),
        "matches": matches, "paired_residues": paired, "alignment_columns": columns,
        "identity_paired_percent": round(100 * matches / paired, 2),
        "identity_columns_percent": round(100 * matches / columns, 2),
        "query_paired_coverage_percent": round(100 * paired / len(query), 2),
        "target_paired_coverage_percent": round(100 * paired / len(target), 2),
        "query_span": [int(x) for x in alignment.coordinates[1, [0, -1]]],
        "target_span": [int(x) for x in alignment.coordinates[0, [0, -1]]],
    }
    return result, str(alignment)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("fasta", type=Path, help="two records: query, then target")
    parser.add_argument("--mode", choices=["local", "global"], default="local")
    parser.add_argument("--max-length", type=int, default=5000)
    parser.add_argument("--show", action="store_true", help="print alignment to stderr")
    args = parser.parse_args()
    try:
        result, alignment = compare(args.fasta, args.mode, args.max_length)
    except (OSError, ValueError) as error:
        print(f"ERROR: {error}", file=sys.stderr)
        return 1
    if args.show:
        print(alignment, file=sys.stderr)
    print(json.dumps(result, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Bio.SeqIO.parse reads FASTA records. The script reads at most three records to reject files that contain more than the required pair. Biopython’s Alignment.aligned property identifies paired sequence blocks, and alignment.length counts the printed alignment columns. The reported matches and coverage follow the definitions in the table above.

The script selects the first returned optimal alignment without enumerating alternatives. Ties can differ in gap placement or selected coordinates; keep the Biopython version and scoring settings with the report. Percentages are rounded to two decimal places, and the integer counts remain available for recalculation.

6 Fix invalid inputs and interpret results

Error or result Action
supply exactly two FASTA records Extract the intended pair, with query first and target second. Keep a copy of the source records.
record IDs must be nonempty and distinct Give both records stable, distinct identifiers; duplicate header IDs make reports ambiguous.
unsupported symbols Review the sequence policy. Resolve ambiguous residues or choose a tool and scoring policy that explicitly support them; do not silently delete symbols.
no positive-scoring local alignment Check the inputs and scoring policy. A global comparison answers a different question; do not switch modes solely to force a match.
sequence length must be 1..5000 Check for an empty sequence or unintended long input. The limit is a guard for a small pairwise example, not a performance guarantee.
High identity with low coverage Inspect the spans, domains, and full sequence lengths before making a whole-protein claim.

The script uses dynamic programming for one sequence pair. Large inputs can consume substantial time and memory, even below the configurable limit. For database searches, use a search workflow designed for that scale. This example does not cluster a dataset, verify every train/test sequence pair, or supply a universal threshold for biological function.

If you use sequence similarity to design a machine-learning split, document the alignment mode, matrix, gap policy, identity denominator, and minimum coverage of both sequences before looking at model scores. Apply the same definitions to the cross-subset comparisons you actually perform. Distinct cluster labels alone do not establish an identity ceiling.

Summary

Report exact matches, the identity denominator, and coverage for both proteins together. Local matches and gapped global alignments can share the same paired-residue identity while describing different amounts of sequence. Preserve the input records and scoring settings so another reader can reproduce the comparison.

The fixtures and cover are illustrative; they do not represent experimental protein structures or a biological benchmark.

标签

1024 ablang adsense agents.md ai ai-agent ai-agents ai-seo algorithm amp antibodies apparmor argparse automation background backup bash batch-processing binarycif bioinformatics biopython blockchain boltz bootstrapping boxes bubblewrap c-index cca cdn checkpoint cli cloudflare codex cofoldarena copy cpu monitoring cron csv cuda curl data-leakage data-processing data-quality data-validation datascience datavisualization datetime debugging decimal deployment desktop-app devtools disk-space disown dns docker dovecot download economics electron encoding esm esm2 esm3 esmc esmfold2 fable faceswap fasta fastmcp faulthandler ffmpeg file-descriptors file-io flashppi flask folium frontend game generator git github-actions google google-research grep gzip harness hls html http hugo imports indexnow javascript jev json jsonl just k-means kaggle langfuse leecode linux list litellm llm llms.txt logging logs lollipop lsof m3u8 machine-learning macos manacher matplotlib mcp mirror mmcif model-evaluation monitoring mp3 mp4 mpnn multiomics mutation mysql nanobert nanobodies networkx nginx normalize numpy ollama omegatherm pandas password pathlib pdb pep-723 phaser pillow pip postfix preprocessing print probability protein-alignment protein-design protein-embeddings protein-interactions protein-language-models protein-stability protein-structure proxy pydantic pyecharts pyqt python python3 r raincloud reproducibility requests reservoir-sampling rfoptimization rg ripgrep rosettafold3 roundcube rrsi rsi rsync s-tui sampling scale scikit-learn scrapy screen seaborn security seo sequence-identity sha256 shell signals sklearn socket solana somaticsignatures spl sqlite ssh standardize static-site subprocess symlink sysadmin system-one tarfile tcp tensorflow tkinter tls toml trade tron tronpy troubleshooting turtle typesafe-ai ubuntu unicode usdt utf-8 uv vhhbert vite wallet webp wordcloud wordpress workflow yaml zipfile zoneinfo 迅雷解析

友情链接

其它