$npx -y skills add GPTomics/bioSkills --skill msa-parsingParse and analyze multiple sequence alignments using Biopython. Extract sequences, identify conserved regions, analyze gaps, work with annotations, and manipulate alignment data for downstream analysis. Use when parsing or manipulating multiple sequence alignments.
| 1 | ## Version Compatibility |
| 2 | |
| 3 | Reference examples tested with: BioPython 1.83+, numpy 1.26+ |
| 4 | |
| 5 | Before using code patterns, verify installed versions match. If versions differ: |
| 6 | - Python: `pip show <package>` then `help(module.function)` to check signatures |
| 7 | |
| 8 | If code throws ImportError, AttributeError, or TypeError, introspect the installed |
| 9 | package and adapt the example to match the actual API rather than retrying. |
| 10 | |
| 11 | # MSA Parsing and Analysis |
| 12 | |
| 13 | Parse multiple sequence alignments to extract information, analyze content, and prepare for downstream analysis. |
| 14 | |
| 15 | ## Required Import |
| 16 | |
| 17 | **Goal:** Load modules for parsing, analyzing, and manipulating multiple sequence alignments. |
| 18 | |
| 19 | **Approach:** Import AlignIO for reading, Counter for column analysis, and alignment classes for constructing modified alignments. |
| 20 | |
| 21 | ```python |
| 22 | from Bio import AlignIO |
| 23 | from Bio.Align import MultipleSeqAlignment |
| 24 | from Bio.SeqRecord import SeqRecord |
| 25 | from Bio.Seq import Seq |
| 26 | from collections import Counter |
| 27 | import numpy as np |
| 28 | import pandas as pd |
| 29 | ``` |
| 30 | |
| 31 | Optional for streaming and Easel-based weighting: |
| 32 | ```python |
| 33 | import pyhmmer |
| 34 | ``` |
| 35 | |
| 36 | ## Loading Alignments |
| 37 | |
| 38 | **Goal:** Read an MSA file and inspect its dimensions. |
| 39 | |
| 40 | **Approach:** Use `AlignIO.read()` specifying the file and format. |
| 41 | |
| 42 | ```python |
| 43 | from Bio import AlignIO |
| 44 | |
| 45 | alignment = AlignIO.read('alignment.fasta', 'fasta') |
| 46 | print(f'{len(alignment)} sequences, {alignment.get_alignment_length()} columns') |
| 47 | ``` |
| 48 | |
| 49 | ## Extracting Sequence Information |
| 50 | |
| 51 | ### Get All Sequence IDs |
| 52 | ```python |
| 53 | seq_ids = [record.id for record in alignment] |
| 54 | ``` |
| 55 | |
| 56 | ### Get Sequences as Strings |
| 57 | ```python |
| 58 | sequences = [str(record.seq) for record in alignment] |
| 59 | ``` |
| 60 | |
| 61 | ### Get Sequence by ID |
| 62 | ```python |
| 63 | def get_sequence_by_id(alignment, seq_id): |
| 64 | for record in alignment: |
| 65 | if record.id == seq_id: |
| 66 | return record |
| 67 | return None |
| 68 | |
| 69 | target = get_sequence_by_id(alignment, 'species_A') |
| 70 | ``` |
| 71 | |
| 72 | ### Access Descriptions and Annotations |
| 73 | ```python |
| 74 | for record in alignment: |
| 75 | print(f'ID: {record.id}') |
| 76 | print(f'Description: {record.description}') |
| 77 | print(f'Annotations: {record.annotations}') |
| 78 | ``` |
| 79 | |
| 80 | ## Column-wise Analysis |
| 81 | |
| 82 | **Goal:** Analyze alignment content column by column to assess composition, conservation, and variability. |
| 83 | |
| 84 | **Approach:** Use column indexing (`alignment[:, idx]`) and Counter to examine character frequencies at each position. |
| 85 | |
| 86 | ### Get Single Column |
| 87 | ```python |
| 88 | column_5 = alignment[:, 5] # Returns string of characters at position 5 |
| 89 | print(column_5) # e.g., 'AAAGA' |
| 90 | ``` |
| 91 | |
| 92 | **API note:** `Bio.AlignIO` returns `MultipleSeqAlignment` objects whose `[:, idx]` returns a plain `str`; `[:, start:end]` returns another `MultipleSeqAlignment`. The newer `Bio.Align.Alignment` (from `Align.read` / `Align.parse`) uses numpy-backed slicing -- verify with `type(alignment[:, 0])` before assuming string methods work. For numpy-array access to the full alignment, use `np.array(alignment)`. |
| 93 | |
| 94 | ### Iterate and Count Columns |
| 95 | ```python |
| 96 | for col_idx in range(alignment.get_alignment_length()): |
| 97 | column = alignment[:, col_idx] |
| 98 | counts = Counter(column) |
| 99 | ``` |
| 100 | |
| 101 | ### Find Conserved Positions |
| 102 | ```python |
| 103 | def find_conserved_positions(alignment, threshold=1.0): |
| 104 | conserved = [] |
| 105 | for col_idx in range(alignment.get_alignment_length()): |
| 106 | column = alignment[:, col_idx] |
| 107 | counts = Counter(column) |
| 108 | most_common_char, most_common_count = counts.most_common(1)[0] |
| 109 | if most_common_char != '-': |
| 110 | conservation = most_common_count / len(alignment) |
| 111 | if conservation >= threshold: |
| 112 | conserved.append((col_idx, most_common_char)) |
| 113 | return conserved |
| 114 | |
| 115 | fully_conserved = find_conserved_positions(alignment, threshold=1.0) |
| 116 | mostly_conserved = find_conserved_positions(alignment, threshold=0.8) |
| 117 | ``` |
| 118 | |
| 119 | ## Gap Analysis |
| 120 | |
| 121 | **Goal:** Quantify gap distribution across sequences and columns to identify problematic regions or sequences. |
| 122 | |
| 123 | **Approach:** Count gap characters per sequence and per column, then identify positions exceeding a gap fraction threshold. |
| 124 | |
| 125 | ### Count Gaps Per Sequence |
| 126 | ```python |
| 127 | gap_counts = [(record.id, str(record.seq).count('-')) for record in alignment] |
| 128 | for seq_id, gaps in gap_counts: |
| 129 | print(f'{seq_id}: {gaps} gaps') |
| 130 | ``` |
| 131 | |
| 132 | ### Count Gaps Per Column |
| 133 | ```python |
| 134 | def gaps_per_column(alignment): |
| 135 | return [alignment[:, i].count('-') for i in range(alignment.get_alignment_length())] |
| 136 | |
| 137 | gap_profile = gaps_per_column(alignment) |
| 138 | ``` |
| 139 | |
| 140 | ### Find Gappy Columns |
| 141 | ```python |
| 142 | def find_gappy_columns(alignment, threshold=0.5): |
| 143 | gappy = [] |
| 144 | num_seqs = len(alignment) |
| 145 | for col_idx in range(alignment.get_alignment_length()): |
| 146 | column = alignment[:, col_idx] |
| 147 | gap_fraction = column.c |