sequence-manipulation/codon-usage/SKILL.md
Analyze codon usage and calculate CAI (Codon Adaptation Index), RSCU, and Nc with Biopython, and produce naive max-CAI codon-optimized sequences. Use when scoring a gene's codon bias against a host, optimizing a CDS for heterologous expression, or studying synonymous codon selection.
npx skillsauth add GPTomics/bioSkills bio-codon-usageInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reference examples tested with: BioPython 1.83+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturesIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
Analyze codon usage patterns, score adaptation to a host, and optimize coding sequences for expression.
"Analyze codon usage" -> Count codons in a coding sequence, compute frequencies and bias metrics.
Counter on in-frame triplets + RSCU/Nc helpers (BioPython + standard library)"Score this gene against a host" -> Compute the Codon Adaptation Index from a reference set of highly expressed genes.
CodonAdaptationIndex(reference_seqs).calculate(query) (BioPython)"Optimize codons for expression" -> Replace each codon with the host's single most-preferred synonymous codon.
CodonAdaptationIndex(reference_seqs).optimize(seq) (BioPython)CAI is meaningless without an expression-biased reference. The relative-adaptiveness weights (w) must be built from the highly expressed genes of the TARGET organism (ribosomal proteins, elongation factors). A CAI computed against a whole-genome average, or against the wrong organism, is a number with no biological meaning. There is no bundled reference index in modern Biopython, so the reference set is always the caller's responsibility.
Two silent traps dominate this skill:
calculate blindly steps range(0, len, 3) from position 0; it never checks the reading frame. A frame-shifted CDS returns a plausible CAI computed from garbage codons. Frame correctness is the caller's job (length divisible by 3, starts at the first base of codon 1).optimize() is the textbook max-CAI output (single most-frequent codon per amino acid). It is blind to the translation ramp, 5' mRNA structure, GC extremes, cryptic regulatory elements, and codon-pair bias. It is a starting point to screen, never a final design. Failure is silent: correct protein, high CAI, poor expression.Bio.SeqUtils.CodonUsage and Bio.SeqUtils.CodonUsageIndices were removed in Biopython 1.82. Any code calling generate_index(), set_cai_index(), cai_for_gene(), print_index(), or importing SharpEcoliIndex raises ImportError on any modern install. The replacement is a redesigned class imported directly from Bio.SeqUtils:
from Bio.SeqUtils import CodonAdaptationIndex # NOT Bio.SeqUtils.CodonUsage (removed)
| Removed (<=1.81) | Replacement (>=1.82) |
|------------------|----------------------|
| CodonAdaptationIndex() then generate_index(fasta) | CodonAdaptationIndex(reference_seqs, table=...) constructor |
| cai.cai_for_gene(seq) | cai.calculate(seq) |
| cai.set_cai_index(d) | cai.update(d) (it is a dict subclass) |
| SharpEcoliIndex (bundled) | none -- build from a supplied reference CDS set |
| cai.print_index() | iterate the object: for codon, w in cai.items() |
from Bio import SeqIO
from Bio.Seq import Seq
from Bio.SeqUtils import CodonAdaptationIndex, GC123
from Bio.Data.CodonTable import standard_dna_table
from Bio.Data import CodonTable
from collections import Counter
Goal: Measure how closely a gene's codon usage matches the highly expressed genes of a host organism.
Approach: Build a CodonAdaptationIndex from a reference set of highly expressed CDS (the constructor computes per-codon relative adaptiveness w), then score query sequences with calculate (0-1, higher = better adapted).
from Bio import SeqIO
from Bio.Seq import Seq
from Bio.SeqUtils import CodonAdaptationIndex
from Bio.Data.CodonTable import standard_dna_table
# Reference = highly expressed genes of the TARGET host (ribosomal proteins, EFs).
# Parse a FASTA yourself; there is no bundled index. Pass str/Seq/SeqRecord.
reference_seqs = list(SeqIO.parse('highly_expressed_genes.fasta', 'fasta'))
cai = CodonAdaptationIndex(reference_seqs, table=standard_dna_table)
query = Seq('ATGAAACGTGCTGAAGCTAAATAA')
score = cai.calculate(query) # 0-1; the query MUST be in-frame (see governing principle)
print(f'CAI: {score:.3f}')
CodonAdaptationIndex is a dict subclass -- the codon->w mapping is the object itself. Inspect or override weights directly:
print(cai['GCT']) # relative adaptiveness of Ala codon GCT
cai.update({'GCT': 0.9}) # override a weight (replaces the old set_cai_index)
Verified behavior (Biopython >=1.82):
calculate uppercase internally.ValueError; an illegal or trailing-partial codon in a query raises TypeError. Out-of-frame input does NOT raise -- it is silently mis-scored.CAI weights come from RSCU. w_ij = RSCU_ij / RSCU_jmax = (codon count) / (count of the most-used synonymous codon in that family); CAI = exp((1/L) * sum ln w). RSCU itself = observed count / expected-if-uniform within a synonymous family (=1 no bias, >1 over-used, <1 under-used). RSCU normalizes away amino-acid composition, which is why w is built from RSCU ratios rather than raw frequencies.
Goal: Quantify synonymous codon bias to detect translational selection or mutational pressure.
Approach: Group codons by amino acid via the codon table, then divide each codon's observed count by the family mean.
from Bio.Data import CodonTable
from collections import Counter
def count_codons(seq):
s = str(seq).upper()
return Counter(s[i:i+3] for i in range(0, len(s) - 2, 3))
def calculate_rscu(seq, table_id=1):
'''RSCU per codon: observed / expected-if-uniform within its synonymous family'''
table = CodonTable.unambiguous_dna_by_id[table_id]
counts = count_codons(seq)
back_table = {}
for codon, aa in table.forward_table.items():
back_table.setdefault(aa, []).append(codon)
rscu = {}
for aa, codons in back_table.items():
total = sum(counts.get(c, 0) for c in codons)
expected = total / len(codons) if codons else 0
for codon in codons:
rscu[codon] = counts.get(codon, 0) / expected if expected > 0 else 0
return rscu
Goal: Generate a host-adapted CDS that preserves the protein.
Approach: optimize() swaps each amino acid for the host's single most-preferred synonymous codon (max-CAI). Always confirm the protein is unchanged, then screen the design against the tradeoffs below.
opt = cai.optimize(query, seq_type='DNA', strict=True)
assert opt.translate() == query.translate() # protein must be identical
optimize(sequence, seq_type='DNA'|'RNA'|'protein', strict=True): strict=True raises ValueError on a tie (two equally-preferred codons, e.g. 'TTT and TTC are equally preferred.'); strict=False warns and picks one.
optimize() is blind to everything except single-codon frequency. Screen the output for:
The tRNA Adaptation Index (dos Reis et al. 2004) weights each codon by tRNA gene copy number (a proxy for tRNA abundance) scaled by wobble-pairing efficiency at the third position. Where tRNA copy number is a good abundance proxy, tAI tracks expression and elongation speed better than CAI. tAI is not in Biopython -- use the R tAI package or reimplement.
A reference-free bias measure (lower = more biased; range ~20 fully biased to 61 unbiased). The helper below is a simplified per-amino-acid approximation: Wright's published estimator averages the homozygosity F WITHIN each degeneracy class (2-, 3-, 4-, 6-fold) before combining as Nc = 2 + 9/F2 + 1/F3 + 5/F4 + 3/F6. The endpoints agree, but intermediate values will not match codonW/standard Nc when families in a class have unequal F. For comparable Nc, average F by class per Wright (1990) or use codonW.
import math
from Bio.Data import CodonTable
def effective_nc(seq, table_id=1):
table = CodonTable.unambiguous_dna_by_id[table_id]
counts = count_codons(seq)
aa_groups = {}
for codon, aa in table.forward_table.items():
aa_groups.setdefault(aa, []).append(codon)
nc_sum = 0
for aa, codons in aa_groups.items():
n = sum(counts.get(c, 0) for c in codons)
if n <= 1:
continue
pi_sq = sum((counts.get(c, 0) / n) ** 2 for c in codons)
F = (n * pi_sq - 1) / (n - 1)
nc_sum += 1 / F if F > 0 else len(codons)
return nc_sum if nc_sum > 0 else 61
from Bio.SeqUtils import GC123
gc_total, gc_pos1, gc_pos2, gc_pos3 = GC123(seq) # four PERCENTAGES (0-100)
print(f'GC3 (wobble): {gc_pos3:.1f}%') # correlates with genome GC
GC123 returns percentages (0-100), unlike gc_fraction which returns 0-1. GC3 at the wobble position usually tracks overall genome GC content.
from Bio.Data import CodonTable
table = CodonTable.unambiguous_dna_by_id[1] # standard genetic code
print(table.start_codons, table.stop_codons)
print(table.forward_table['ATG']) # 'M'
| ID | Name | Organism | |----|------|----------| | 1 | Standard | Most nuclear genomes | | 2 | Vertebrate Mitochondrial | Human/mouse mito | | 4 | Mold/Protozoan Mitochondrial | Fungi, protozoa mito | | 5 | Invertebrate Mitochondrial | Insects, worms mito | | 11 | Bacterial/Plastid | E. coli, chloroplasts |
Pass the matching table= to CodonAdaptationIndex when scoring mitochondrial or bacterial genes.
| Metric | Range | Reference needed | Interpretation | |--------|-------|------------------|----------------| | CAI | 0-1 | Highly expressed genes of the host | Higher = better adapted | | RSCU | 0-N | None (within-sequence) | 1 = no bias, >1 over-used | | Nc | ~20-61 | None | Lower = more biased | | GC3 | 0-100% | None | GC at wobble position | | tAI | 0-1 | tRNA gene copy numbers | Higher = better tRNA supply |
| Symptom | Cause | Fix |
|---------|-------|-----|
| ImportError: cannot import name 'CodonUsage' | Bio.SeqUtils.CodonUsage removed in 1.82 | from Bio.SeqUtils import CodonAdaptationIndex |
| AttributeError: 'CodonAdaptationIndex' object has no attribute 'generate_index' | Old API on new class | Build in the constructor; score with calculate |
| Plausible CAI from a frame-shifted CDS | Out-of-frame input silently mis-scored | Confirm frame: length divisible by 3, starts at codon 1 |
| CAI near 1 for every gene | Reference set is whole-genome, not expression-biased | Use only highly expressed genes of the target host |
| ValueError: ... equally preferred | optimize(strict=True) hit a tie | Pass strict=False, or curate weights with update |
| High CAI but poor expression in the lab | Max-CAI ignores ramp / 5' structure / cryptic sites | Screen optimize() output; treat it as a draft |
Sharp PM, Li WH (1987) The codon adaptation index -- a measure of directional synonymous codon usage bias, and its potential applications. Nucleic Acids Res 15(3):1281-1295.
dos Reis M, Savva R, Wernisch L (2004) Solving the riddle of codon usage preferences: a test for translational selection. Nucleic Acids Res 32(17):5036-5044.
Tuller T, Carmi A, Vestsigian K, Navon S, Dorfan Y, Zaborske J, Pan T, Dahan O, Furman I, Pilpel Y (2010) An evolutionarily conserved mechanism for controlling the efficiency of protein translation. Cell 141(2):344-354.
Wright F (1990) The 'effective number of codons' used in a gene. Gene 87(1):23-29.
tools
End-to-end CLIP-seq pipeline from FASTQ to ENCODE-compliant binding sites, single-nucleotide crosslink maps, annotation, motifs, and (optionally) differential binding. Use when running the full Yeo lab eCLIP / iCLIP / iCLIP2 / iCLIP3 / irCLIP / PAR-CLIP analysis with SMInput control, protocol-specific UMI extraction, ENCODE STAR parameters, CLIPper or Skipper peak calling with stringent log2 FC and -log10 p thresholds, IDR rescue and self-consistency QC, and downstream motif registration with mCross or PEKA.
development
Detect, date, and contextualize whole-genome duplication (WGD / paleopolyploidy) events using wgd v2 (Chen et al 2024), KsRates (Sensalari 2022 substitution-rate-corrected Ks dating), DupGen_finder (Qiao 2019), MAPS (Li 2018 phylogenomic), POInT (Conant 2008 ordered-block), SLEDGe (2024 ML-based), Whale.jl (Bayesian DL+WGD), and synteny-anchored paranome construction. Use when identifying ancient polyploidy from Ks distributions and synteny block analysis, positioning WGD events relative to speciation, distinguishing tandem from segmental from WGD duplications, dating the 2R/3R vertebrate / fish / salmonid WGDs, building paranome and Ks-age mixture models, applying KsRates substitution-rate correction across lineages, or testing alternative biased-fractionation / dosage-balance models post-WGD.
tools
Build whole-genome alignments using Progressive Cactus (Armstrong 2020 reference-free clade-level WGA), Minigraph-Cactus (Hickey 2024 pangenome-aware), LASTZ chain/net (UCSC pipeline), MUMmer4 (Marçais 2018 pairwise), minimap2 -x asm5/10/20 (Li 2018 fast pairwise), AnchorWave (Song 2022 WGD-aware), and Mauve / progressiveMauve (bacterial). Operates the HAL toolkit (Hickey 2013) for downstream extraction including halSynteny, halLiftover, halBranchMutations, and hal2maf. Use when constructing multi-species alignments for comparative-annotation projection (TOGA), synteny detection, conservation analyses (phyloP / PhastCons), or pangenome graph construction; selecting between reference-free (Cactus) and reference-anchored (LASTZ chains/nets) approaches; tuning sensitivity for closely vs distantly related genomes; or producing HAL files for genome-wide downstream tools.
development
Detect syntenic blocks and structural rearrangements between genomes using MCScanX (Wang 2012), JCVI/MCScan (Tang 2008 Python), GENESPACE (Lovell 2022) for orthology-anchored riparian visualization, SyRI for structural variation, AnchorWave for sequence-level synteny, i-ADHoRe 3.0 for highly diverged species, SynNet for synteny networks, and ntSynt for multi-genome macrosynteny. Use when identifying collinear gene blocks across species, distinguishing macrosynteny from microsynteny, detecting inversions/translocations/duplications, anchoring orthology in WGD lineages, producing publication riparian plots, computing synteny block age via Ks (cross-references whole-genome-duplication), or running synteny-aware ortholog inference in polyploids.