|
| 1 | +import math |
| 2 | +from collections import Counter, defaultdict |
| 3 | +from collections.abc import Iterator |
| 4 | + |
| 5 | + |
1 | 6 | def normalize_sequence(seq: str) -> str: |
2 | 7 | return seq.upper().replace("U", "T") |
3 | 8 |
|
4 | 9 |
|
| 10 | +AA_TABLE: dict[str, str] = { |
| 11 | + "TTT": "F", "TTC": "F", "TTA": "L", "TTG": "L", |
| 12 | + "CTT": "L", "CTC": "L", "CTA": "L", "CTG": "L", |
| 13 | + "ATT": "I", "ATC": "I", "ATA": "I", "ATG": "M", |
| 14 | + "GTT": "V", "GTC": "V", "GTA": "V", "GTG": "V", |
| 15 | + "TCT": "S", "TCC": "S", "TCA": "S", "TCG": "S", |
| 16 | + "CCT": "P", "CCC": "P", "CCA": "P", "CCG": "P", |
| 17 | + "ACT": "T", "ACC": "T", "ACA": "T", "ACG": "T", |
| 18 | + "GCT": "A", "GCC": "A", "GCA": "A", "GCG": "A", |
| 19 | + "TAT": "Y", "TAC": "Y", "TAA": "*", "TAG": "*", |
| 20 | + "CAT": "H", "CAC": "H", "CAA": "Q", "CAG": "Q", |
| 21 | + "AAT": "N", "AAC": "N", "AAA": "K", "AAG": "K", |
| 22 | + "GAT": "D", "GAC": "D", "GAA": "E", "GAG": "E", |
| 23 | + "TGT": "C", "TGC": "C", "TGA": "*", "TGG": "W", |
| 24 | + "CGT": "R", "CGC": "R", "CGA": "R", "CGG": "R", |
| 25 | + "AGT": "S", "AGC": "S", "AGA": "R", "AGG": "R", |
| 26 | + "GGT": "G", "GGC": "G", "GGA": "G", "GGG": "G", |
| 27 | +} |
| 28 | + |
| 29 | + |
| 30 | +def iter_codons(seq: str) -> Iterator[str]: |
| 31 | + seq = seq.upper().replace("U", "T") |
| 32 | + for i in range(0, len(seq) - 2, 3): |
| 33 | + c = seq[i : i + 3] |
| 34 | + if len(c) == 3 and set(c) <= {"A", "C", "G", "T"}: |
| 35 | + yield c |
| 36 | + |
| 37 | + |
| 38 | +def make_aa_to_codons(codon_table: dict[str, str] | None = None) -> dict[str, list[str]]: |
| 39 | + if codon_table is None: |
| 40 | + codon_table = AA_TABLE |
| 41 | + result: dict[str, list[str]] = defaultdict(list) |
| 42 | + for codon, aa in codon_table.items(): |
| 43 | + if aa != "*": |
| 44 | + result[aa].append(codon) |
| 45 | + return dict(result) |
| 46 | + |
| 47 | + |
| 48 | +def calculate_cai( |
| 49 | + counts: Counter[str], |
| 50 | + ref_counts: Counter[str], |
| 51 | + aa_to_codons: dict[str, list[str]], |
| 52 | +) -> float: |
| 53 | + w_values: dict[str, float] = {} |
| 54 | + for _aa, codons in aa_to_codons.items(): |
| 55 | + max_count = max((ref_counts[c] for c in codons), default=0) |
| 56 | + if max_count == 0: |
| 57 | + for c in codons: |
| 58 | + w_values[c] = 1.0 |
| 59 | + else: |
| 60 | + for c in codons: |
| 61 | + w_values[c] = ref_counts[c] / max_count |
| 62 | + |
| 63 | + log_sum = 0.0 |
| 64 | + total = 0 |
| 65 | + for codon, count in counts.items(): |
| 66 | + if codon in w_values and count > 0: |
| 67 | + log_sum += count * math.log(w_values[codon]) if w_values[codon] > 0 else 0 |
| 68 | + total += count |
| 69 | + |
| 70 | + return math.exp(log_sum / total) if total > 0 else 0.0 |
| 71 | + |
| 72 | + |
5 | 73 | def gc_content(seq: str) -> float: |
6 | 74 | seq = normalize_sequence(seq) |
7 | 75 | g = seq.count("G") |
|
0 commit comments