Skip to content
Back to skills

Python Bio Comprehensions

ASecurity

Python comprehensions and generators for bioinformatics — list/dict/set comprehensions, generator expressions for large sequence files, bio-specific patterns.

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aipythonexpress

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Pavel-Kravchenko/Bioinformatics --skill python-bio-comprehensions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Python Bio Comprehensions?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Python Bio Comprehensions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pavel-kravchenko-python-bio-comprehensions/badge)](https://www.skillsdirectory.com/skills/pavel-kravchenko-python-bio-comprehensions)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: python-bio-comprehensions
description: Python comprehensions and generators for bioinformatics — list/dict/set comprehensions, generator expressions for large sequence files, bio-specific patterns.
tool_type: python
primary_tool: Python
---

# Python Comprehensions for Bioinformatics

## Pitfalls

- **Nested comprehension loop order**: In a flat nested comprehension, outer loop comes first — `[expr for outer in outer_list for inner in inner_list]`. Use `[[expr for inner in ...] for outer in ...]` for list-of-lists.
- **`if` filter vs `if`/`else` transform**: `if` after `for` is a filter (removes items). `if`/`else` in the expression is a conditional transform (keeps all items, maps differently).
- **Generator expressions are single-pass**: Once consumed (`sum(...)`, `list(...)`, etc.), the generator is exhausted. Use a list comprehension when you need multiple passes.
- **Dict comprehension with `.count()` is O(n²)**: `{kmer: seq.count(kmer) for kmer in set(kmers)}` recomputes count for every unique key. Use `collections.Counter` instead.
- **Off-by-one errors**: Python ranges are half-open `[start, stop)` — bioinformatics coordinates are often 1-based.
- **Deep vs shallow copy**: `list.copy()` and `[:]` only copy the top level. Use `copy.deepcopy()` for nested structures.

## Quick Reference

```python
# Filter: keeps only G and C
[nt for nt in dna if nt in 'GC']

# Transform: relabels every base
["purine" if nt in "AG" else "pyrimidine" for nt in dna]

# Filter AND transform: keep long seqs, return (name, gc)
[(name, gc(seq)) for name, seq in fasta.items() if len(seq) > 100]

# Dict comprehension
{name: gc(seq) for name, seq in fasta.items()}

# Set comprehension
{aa for aa in protein}

# Generator (lazy, memory-efficient)
avg_gc = sum(gc(s) for s in sequences) / len(sequences)
```

## Bio-Specific Patterns

```python
# GC content
def gc_content(seq):
    return (seq.count('G') + seq.count('C')) / len(seq)

# Codon splitting
codons = [cds[i:i+3] for i in range(0, len(cds) - len(cds) % 3, 3)]

# Translation (up to first stop)
protein = ''.join(
    aa for aa in (GENETIC_CODE.get(c, 'X') for c in codons)
    if aa != '*'
)

# Reverse complement
rc_map = {'A':'T','T':'A','G':'C','C':'G'}
rev_comp = ''.join(rc_map[n] for n in seq[::-1])

# Sliding window GC profile
window = 100
gc_profile = [(i, (seq[i:i+window].count('G') + seq[i:i+window].count('C')) / window)
              for i in range(len(seq) - window + 1)]

# Three reading frames (list of lists)
frames = [[dna[i:i+3] for i in range(f, len(dna)-2, 3)] for f in range(3)]

# Flatten all codons across frames
all_codons = [codon for frame in frames for codon in frame]

# All k-mers of length k
from itertools import product
all_kmers = [''.join(c) for c in product('ATGC', repeat=k)]

# Unique k-mer spectrum
spectrum = sorted({seq[i:i+k] for i in range(len(seq) - k + 1)})

# Codons encoding a given amino acid
leu_codons = {codon for codon, aa in GENETIC_CODE.items() if aa == 'L'}

# Filter FASTA by GC and length
filtered = {name: seq for name, seq in fasta.items()
            if len(seq) > 100 and 0.4 <= gc_content(seq) <= 0.6}

# Sequences with GC > 55% — generator, no intermediate list
high_gc_count = sum(1 for seq in sequences if gc_content(seq) > 0.55)

# Gene-to-pathway reverse map
pathways = {'DNA_repair': {'BRCA1','BRCA2','ATM'}, 'Cell_cycle': {'TP53','RB1','CDK4'}}
gene_to_pathways = {gene: [p for p, genes in pathways.items() if gene in genes]
                    for gene in {g for genes in pathways.values() for g in genes}}

# any/all with generators
any_pure_gc  = any(set(s) <= {'G', 'C'} for s in sequences)
all_valid    = all(set(s) <= {'A', 'T', 'G', 'C'} for s in sequences)
```

## Memory: Generator vs List

```python
import sys
n = 100_000
list_comp = [x**2 for x in range(n)]   # ~800 KB in memory
gen_expr  = (x**2 for x in range(n))   # ~200 bytes
# Use generator when: processing genome-scale data, streaming files, passing to sum/max/any/all
# Use list when: you need multiple passes, indexing, or len()
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.