Spaces:
Sleeping
Sleeping
| """Homopolymer detection in nucleotide sequences.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import List | |
| class HomopolymerRun: | |
| nucleotide: str # the repeated base ("A", "T", "G", "C") | |
| start: int # 0-based start position | |
| end: int # 0-based end position (exclusive) | |
| length: int | |
| def __repr__(self) -> str: | |
| return f"HomopolymerRun({self.nucleotide!r} ×{self.length} @ [{self.start}:{self.end}])" | |
| def detect_homopolymers( | |
| sequence: str, | |
| min_run: int = 5, | |
| bases: str = "ATGC", | |
| ) -> List[HomopolymerRun]: | |
| """ | |
| Detect homopolymer runs (consecutive identical nucleotides) in a sequence. | |
| Parameters | |
| ---------- | |
| sequence : str | |
| Nucleotide sequence (DNA or RNA). | |
| min_run : int | |
| Minimum run length to report (default 5). | |
| bases : str | |
| Which bases to check. Default "ATGC" checks all. Use "A" to detect | |
| only poly-A, for example. | |
| Returns | |
| ------- | |
| List[HomopolymerRun] | |
| Sorted by start position. | |
| """ | |
| seq = sequence.upper().replace("U", "T") | |
| if not seq: | |
| return [] | |
| runs: List[HomopolymerRun] = [] | |
| i = 0 | |
| n = len(seq) | |
| while i < n: | |
| base = seq[i] | |
| if base not in bases: | |
| i += 1 | |
| continue | |
| j = i | |
| while j < n and seq[j] == base: | |
| j += 1 | |
| run_len = j - i | |
| if run_len >= min_run: | |
| runs.append(HomopolymerRun( | |
| nucleotide=base, | |
| start=i, | |
| end=j, | |
| length=run_len, | |
| )) | |
| i = j | |
| return runs | |
| def homopolymer_summary(runs: List[HomopolymerRun]) -> dict: | |
| """Summarise a list of HomopolymerRun objects.""" | |
| if not runs: | |
| return {"count": 0, "max_length": 0, "longest": None} | |
| longest = max(runs, key=lambda r: r.length) | |
| by_base = {} | |
| for r in runs: | |
| by_base.setdefault(r.nucleotide, []).append(r) | |
| return { | |
| "count": len(runs), | |
| "max_length": longest.length, | |
| "longest": longest, | |
| "by_base": {b: len(v) for b, v in by_base.items()}, | |
| } | |