| name | python-bio-lists |
| description | Split CDS into codons, extract k-mers, sort sequences by GC%/length, and pack gene coordinates into tuples/namedtuples. Use when looping over genes/codons/SNPs/BED intervals, computing sliding-window GC%, or detecting gene overlaps in Python. |
| tool_type | python |
| primary_tool | Python |
Lists and Tuples for Bioinformatics
When to Use
- Splitting a coding sequence (CDS) into codons or extracting sliding k-mers from DNA/RNA.
- Sorting a batch of sequences or genes by GC content, length, or any derived key.
- Representing a fixed biological record (gene coordinates, a SNP call, a BED interval) that should not be accidentally mutated.
- Building readable gene/variant records with
namedtuple instead of unlabeled index access (gene[2]).
- Detecting overlaps between genomic intervals stored as
(name, start, end) tuples.
Version Compatibility
Pure Python stdlib — no third-party dependency. Works on Python ≥3.8 (relies on collections.namedtuple, f-strings, and stable dict ordering; all present since 3.7+).
Prerequisites
- No packages to install — everything here is Python builtins plus
collections from the standard library.
- Familiarity with string indexing/slicing (see
python-bio-strings) and basic for loops.
Goal: split a CDS into codons and pull every k-mer of length k out of a sequence.
Approach: step the index by 3 (codons) or by 1 (k-mers), slicing the string at each position; guard against a trailing partial codon with len(seq) % 3.
def split_codons(cds: str) -> list[str]:
"""Split a coding DNA sequence into complete triplet codons.
Any trailing 1-2 leftover bases (incomplete codon) are dropped.
"""
return [cds[i:i + 3] for i in range(0, len(cds) - len(cds) % 3, 3)]
def extract_kmers(sequence: str, k: int) -> list[str]:
"""Return all overlapping k-mers of length k, in order of appearance."""
return [sequence[i:i + k] for i in range(len(sequence) - k + 1)]
cds = "ATGGCCGATCGATAGCCA"
print(split_codons(cds))
print(extract_kmers("ATGATGATG", 3))
Goal: rank a collection of sequences or genes by a biological property (GC%, length).
Approach: write a small key function and pass it to sorted(..., key=...) — never mutate the input with .sort() unless you explicitly want that.
def gc_content(seq: str) -> float:
"""Fraction of G/C bases in seq, in [0, 1]."""
return (seq.count("G") + seq.count("C")) / len(seq)
sequences = ["ATATATAT", "GCGCGCGC", "ATGCATGC", "AAAGGGCCC", "TTTTAAAA"]
by_gc = sorted(sequences, key=gc_content)
by_gc_desc = sorted(sequences, key=gc_content, reverse=True)
gene_data = [("BRCA1", 81189), ("TP53", 19149), ("EGFR", 188307)]
longest = max(gene_data, key=lambda g: g[1])
by_length = sorted(gene_data, key=lambda g: g[1], reverse=True)
Goal: store fixed genomic records (gene coordinates) and find overlapping intervals.
Approach: use plain tuples for lightweight (name, start, end) records, namedtuple when you want .start/.end field access, and a simple O(n^2) pairwise scan for overlap detection on small interval lists (for large interval sets use an interval tree — see bio-genome-intervals-interval-arithmetic).
from collections import namedtuple
Gene = namedtuple("Gene", ["name", "chromosome", "start", "end", "strand"])
brca1 = Gene("BRCA1", "chr17", 43044295, 43125483, "-")
print(brca1.end - brca1.start)
def find_overlapping_genes(genes: list[tuple[str, int, int]]) -> list[tuple[str, str, int]]:
"""Find all pairs of overlapping (name, start, end) intervals.
Returns [(name_a, name_b, overlap_bp), ...] for every overlapping pair.
O(n^2) — fine for dozens to low thousands of intervals.
"""
overlaps = []
for i in range(len(genes)):
for j in range(i + 1, len(genes)):
name_a, start_a, end_a = genes[i]
name_b, start_b, end_b = genes[j]
overlap_start = max(start_a, start_b)
overlap_end = min(end_a, end_b)
if overlap_start < overlap_end:
overlaps.append((name_a, name_b, overlap_end - overlap_start))
return overlaps
test_genes = [("geneA", 100, 500), (, , ), (, , )]
(find_overlapping_genes(test_genes))
Pitfalls
- Assignment does not copy:
alias = original makes both names point to the same list; alias.append(...) mutates original too. Use original.copy() or original[:] for an independent copy.
append() vs extend(): genes.append(["BRCA1", "TP53"]) adds a list as one element; genes.extend(["BRCA1", "TP53"]) adds two elements.
sort() returns None: it mutates in place and returns None — chaining x = my_list.sort() silently gives x = None. Use sorted() when you need the original preserved.
- Trailing comma required for a 1-tuple:
("BRCA1") is just a string; ("BRCA1",) is a tuple.
- Off-by-one on the last codon/k-mer: forgetting
- len(seq) % 3 (codons) or - k + 1 (k-mers) either crashes with a short slice or silently drops the final complete unit.
- Tuples are immutable but hashable: use them for fixed records (coordinates, SNPs) and as dict keys; lists cannot be dict keys.
See Also
python-bio-tuples — deeper dive on packing/unpacking and named tuples.
python-bio-dictionaries — pairing these records with fast name/coordinate lookups.
python-bio-comprehensions — list/dict comprehensions for the same codon/k-mer patterns.
bio-genome-intervals-interval-arithmetic — scalable interval overlap for large BED-style datasets.