Source: https://github.com/aipoch/medical-research-skills
biopython-alignment
When to Use
- You need global alignment between two protein (or nucleotide) sequences and want a reproducible score and aligned strings.
- You need local alignment to find the best matching fragment/subsequence between two DNA/RNA/protein sequences.
- You need to read, write, or convert multiple sequence alignment (MSA) files (e.g., FASTA/Clustal/Stockholm) using Biopython I/O.
- You want to compute alignment statistics (e.g., identity, coverage, conservation per column) and filter alignments by thresholds.
- You need to apply substitution matrices (e.g., BLOSUM62) and tune gap penalties for biologically meaningful scoring.
Key Features
- Pairwise alignment via
Bio.Align.PairwiseAligner (global and local modes).
- Alignment scoring with configurable match/mismatch and gap penalties.
- Protein substitution matrices via
Bio.Align.substitution_matrices (e.g., BLOSUM/PAM).
- MSA parsing and serialization via
Bio.AlignIO (read/write/format conversion).
- Basic alignment statistics: identity, aligned length, coverage, and MSA column conservation.
Dependencies
biopython>=1.81
numpy>=1.21
Example Usage
# -*- coding: utf-8 -*-
"""
Runnable examples for:
1) Global protein alignment
2) Local DNA alignment (best fragment)
3) MSA parsing + column conservation
Requires: biopython, numpy
"""
from __future__ import annotations
from io import StringIO
import numpy as np
from Bio.Align import PairwiseAligner
from Bio.Align import substitution_matrices
from Bio import AlignIO
def global_protein_alignment(seq_a: str, seq_b: str) -> None:
matrix = substitution_matrices.load("BLOSUM62")
aligner = PairwiseAligner()
aligner.mode = "global"
aligner.substitution_matrix = matrix
aligner.open_gap_score = -10.0
aligner.extend_gap_score = -0.5
alignments = aligner.align(seq_a, seq_b)
best = alignments[0]
print("=== Global protein alignment (best) ===")
print("Score:", best.score)
print(best)
def local_dna_alignment_best_fragment(seq_a: str, seq_b: str) -> None:
aligner = PairwiseAligner()
aligner.mode = "local"
aligner.match_score = 2.0
aligner.mismatch_score = -1.0
aligner.open_gap_score = -2.0
aligner.extend_gap_score = -0.5
best = aligner.align(seq_a, seq_b)[0]
# Extract the aligned fragment coordinates from the first aligned block.
# aligned is a tuple: (aligned_coords_in_seq_a, aligned_coords_in_seq_b)
a_blocks, b_blocks = best.aligned
a_start, a_end = a_blocks[0]
b_start, b_end = b_blocks[0]
print("=== Local DNA alignment (best) ===")
print("Score:", best.score)
print(best)
print("Best fragment in seq_a:", seq_a[a_start:a_end], f"(coords {a_start}:{a_end})")
print("Best fragment in seq_b:", seq_b[b_start:b_end], f"(coords {b_start}:{b_end})")
def msa_column_conservation(fasta_text: str) -> None:
handle = StringIO(fasta_text)
msa = AlignIO.read(handle, "fasta") # MultipleSeqAlignment
# Convert to a 2D array of characters: shape (n_seqs, aln_len)
arr = np.array([list(str(rec.seq)) for rec in msa], dtype="U1")
n_seqs, aln_len = arr.shape
# Conservation per column: fraction of the most common non-gap character.
# Treat '-' as gap; ignore gaps when computing the most common residue.
conservation = []
for j in range(aln_len):
col = arr[:, j]
col = col[col != "-"]
if col.size == 0:
conservation.append(0.0)
continue
values, counts = np.unique(col, return_counts=True)
conservation.append(float(counts.max() / counts.sum()))
print("=== MSA column conservation ===")
print("n_seqs:", n_seqs, "aln_len:", aln_len)
print("conservation:", [round(x, 3) for x in conservation])
def main() -> None:
# 1) Global alignment (protein)
seq_a = "MKTAYIAKQRQISFVKSHFSRQDILD"
seq_b = "MKLAYIAKQRQISFVKSHFTRQDILN"
global_protein_alignment(seq_a, seq_b)
# 2) Local alignment (DNA)
seq_a = "ATGCGTACGTTAGC"
seq_b = "GGGATGCGTACGAAAC"
local_dna_alignment_best_fragment(seq_a, seq_b)
# 3) MSA conservation (FASTA)
fasta_text = ">s1\nACGTACGT\n>s2\nACGTTCGT\n>s3\nACGTACGA\n"
msa_column_conservation(fasta_text)
if __name__ == "__main__":
main()
Implementation Details
- Pairwise alignment engine: uses
Bio.Align.PairwiseAligner, which performs dynamic programming alignment under the selected mode:
mode="global": aligns full-length sequences end-to-end.
mode="local": finds the highest-scoring matching region (best subsequence pair).
- Scoring configuration:
- For proteins, prefer
substitution_matrix (e.g., BLOSUM62) plus gap penalties (open_gap_score, extend_gap_score).
- For nucleotides, a simple scheme is common:
match_score, mismatch_score, and gap penalties.
- Selecting the best alignment:
aligner.align(a, b) returns an iterable of alignments sorted by score; use [0] for the top-scoring result.
- Local “best fragment” extraction:
alignment.aligned returns aligned coordinate blocks for each sequence.
- The first block
(start, end) typically corresponds to the highest-scoring contiguous aligned region; slice the original sequences with these coordinates to obtain the fragment.
- MSA I/O and statistics:
Bio.AlignIO.read(handle, fmt) parses an alignment into a MultipleSeqAlignment.
- Column conservation can be computed as:
max_count(non-gap residues in column) / total_non_gap_count(column).
- Operational conventions (recommended):
- Store runtime configuration in
config/task_config.json and invoke scripts as python scripts/<task_name>.py.
- Avoid stacking many CLI
-- parameters; keep parameters in the config file.
- Always specify
encoding="utf-8" for file I/O; for JSON output use ensure_ascii=False.
1---2name: biopython-alignment3description: Sequence alignment and alignment file processing with Biopython (Bio.Align/Bio.AlignIO), triggered when you need global/local pairwise alignment, MSA read/write/format conversion, or alignment statistics/filtering.4license: MIT5---6> **Source**: [https://github.com/aipoch/medical-research-skills](https://github.com/aipoch/medical-research-skills)
7
8# biopython-alignment
9
10## When to Use
11
12- You need **global alignment** between two protein (or nucleotide) sequences and want a reproducible score and aligned strings.
13- You need **local alignment** to find the best matching fragment/subsequence between two DNA/RNA/protein sequences.
14- You need to **read, write, or convert** multiple sequence alignment (MSA) files (e.g., FASTA/Clustal/Stockholm) using Biopython I/O.
15- You want to compute **alignment statistics** (e.g., identity, coverage, conservation per column) and filter alignments by thresholds.
16- You need to apply **substitution matrices** (e.g., BLOSUM62) and tune gap penalties for biologically meaningful scoring.
17
18## Key Features
19
20- Pairwise alignment via `Bio.Align.PairwiseAligner` (global and local modes).
21- Alignment scoring with configurable match/mismatch and gap penalties.
22- Protein substitution matrices via `Bio.Align.substitution_matrices` (e.g., BLOSUM/PAM).
23- MSA parsing and serialization via `Bio.AlignIO` (read/write/format conversion).
24- Basic alignment statistics: identity, aligned length, coverage, and MSA column conservation.
25
26## Dependencies
27
28- `biopython>=1.81`
29- `numpy>=1.21`
30
31## Example Usage
32
33```python
34# -*- coding: utf-8 -*-
35"""
36Runnable examples for:
371) Global protein alignment
382) Local DNA alignment (best fragment)
393) MSA parsing + column conservation
40
41Requires: biopython, numpy
42"""
43
44from __future__ import annotations
45
46from io import StringIO
47import numpy as np
48
49from Bio.Align import PairwiseAligner
50from Bio.Align import substitution_matrices
51from Bio import AlignIO
52
53
54def global_protein_alignment(seq_a: str, seq_b: str) -> None:
55 matrix = substitution_matrices.load("BLOSUM62")
56
57 aligner = PairwiseAligner()
58 aligner.mode = "global"
59 aligner.substitution_matrix = matrix
60 aligner.open_gap_score = -10.0
61 aligner.extend_gap_score = -0.5
62
63 alignments = aligner.align(seq_a, seq_b)
64 best = alignments[0]
65
66 print("=== Global protein alignment (best) ===")
67 print("Score:", best.score)
68 print(best)
69
70
71def local_dna_alignment_best_fragment(seq_a: str, seq_b: str) -> None:
72 aligner = PairwiseAligner()
73 aligner.mode = "local"
74 aligner.match_score = 2.0
75 aligner.mismatch_score = -1.0
76 aligner.open_gap_score = -2.0
77 aligner.extend_gap_score = -0.5
78
79 best = aligner.align(seq_a, seq_b)[0]
80
81 # Extract the aligned fragment coordinates from the first aligned block.
82 # aligned is a tuple: (aligned_coords_in_seq_a, aligned_coords_in_seq_b)
83 a_blocks, b_blocks = best.aligned
84 a_start, a_end = a_blocks[0]
85 b_start, b_end = b_blocks[0]
86
87 print("=== Local DNA alignment (best) ===")
88 print("Score:", best.score)
89 print(best)
90 print("Best fragment in seq_a:", seq_a[a_start:a_end], f"(coords {a_start}:{a_end})")
91 print("Best fragment in seq_b:", seq_b[b_start:b_end], f"(coords {b_start}:{b_end})")
92
93
94def msa_column_conservation(fasta_text: str) -> None:
95 handle = StringIO(fasta_text)
96 msa = AlignIO.read(handle, "fasta") # MultipleSeqAlignment
97
98 # Convert to a 2D array of characters: shape (n_seqs, aln_len)
99 arr = np.array([list(str(rec.seq)) for rec in msa], dtype="U1")
100 n_seqs, aln_len = arr.shape
101
102 # Conservation per column: fraction of the most common non-gap character.
103 # Treat '-' as gap; ignore gaps when computing the most common residue.
104 conservation = []
105 for j in range(aln_len):
106 col = arr[:, j]
107 col = col[col != "-"]
108 if col.size == 0:
109 conservation.append(0.0)
110 continue
111 values, counts = np.unique(col, return_counts=True)
112 conservation.append(float(counts.max() / counts.sum()))
113
114 print("=== MSA column conservation ===")
115 print("n_seqs:", n_seqs, "aln_len:", aln_len)
116 print("conservation:", [round(x, 3) for x in conservation])
117
118
119def main() -> None:
120 # 1) Global alignment (protein)
121 seq_a = "MKTAYIAKQRQISFVKSHFSRQDILD"
122 seq_b = "MKLAYIAKQRQISFVKSHFTRQDILN"
123 global_protein_alignment(seq_a, seq_b)
124
125 # 2) Local alignment (DNA)
126 seq_a = "ATGCGTACGTTAGC"
127 seq_b = "GGGATGCGTACGAAAC"
128 local_dna_alignment_best_fragment(seq_a, seq_b)
129
130 # 3) MSA conservation (FASTA)
131 fasta_text = ">s1\nACGTACGT\n>s2\nACGTTCGT\n>s3\nACGTACGA\n"
132 msa_column_conservation(fasta_text)
133
134
135if __name__ == "__main__":
136 main()
137```
138
139## Implementation Details
140
141- **Pairwise alignment engine**: uses `Bio.Align.PairwiseAligner`, which performs dynamic programming alignment under the selected mode:
142 - `mode="global"`: aligns full-length sequences end-to-end.
143 - `mode="local"`: finds the highest-scoring matching region (best subsequence pair).
144- **Scoring configuration**:
145 - For proteins, prefer `substitution_matrix` (e.g., `BLOSUM62`) plus gap penalties (`open_gap_score`, `extend_gap_score`).
146 - For nucleotides, a simple scheme is common: `match_score`, `mismatch_score`, and gap penalties.
147- **Selecting the best alignment**: `aligner.align(a, b)` returns an iterable of alignments sorted by score; use `[0]` for the top-scoring result.
148- **Local “best fragment” extraction**:
149 - `alignment.aligned` returns aligned coordinate blocks for each sequence.
150 - The first block `(start, end)` typically corresponds to the highest-scoring contiguous aligned region; slice the original sequences with these coordinates to obtain the fragment.
151- **MSA I/O and statistics**:
152 - `Bio.AlignIO.read(handle, fmt)` parses an alignment into a `MultipleSeqAlignment`.
153 - Column conservation can be computed as:
154 `max_count(non-gap residues in column) / total_non_gap_count(column)`.
155- **Operational conventions (recommended)**:
156 - Store runtime configuration in `config/task_config.json` and invoke scripts as `python scripts/<task_name>.py`.
157 - Avoid stacking many CLI `--` parameters; keep parameters in the config file.
158 - Always specify `encoding="utf-8"` for file I/O; for JSON output use `ensure_ascii=False`.