# Python Bio Python Introduction

> Foundational Python for biology - count nucleotides, compute GC content, use f-strings/loops/functions, and set up biopython/pandas/numpy via pip/venv/conda. Use when a beginner asks how to start with Python for bioinformatics, write a first DNA-parsing script, calculate GC content from a raw string, or install BioPython/scanpy/pysam in a virtualenv or conda environment.

- Skill: `pavel-kravchenko/python-bio-python-introduction` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pavel-kravchenko/python-bio-python-introduction`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pavel-kravchenko/python-bio-python-introduction/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: pavel-kravchenko (https://skillmd.com/u/pavel-kravchenko)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pavel-kravchenko/python-bio-python-introduction

---


# Python Introduction for Bioinformatics

## When to Use

- Onboarding someone new to Python who wants a biology-flavored first program (nucleotide counting, GC content).
- Choosing which scientific/bio Python packages to install for a given task (BioPython, pandas, pysam, scanpy, scikit-bio).
- Setting up a reproducible environment with `venv` or `conda` before starting any bioinformatics project.
- Explaining core language pitfalls (f-strings, mutable defaults, 0- vs 1-based coordinates) that trip up biologists writing their first scripts.
- Quick BioPython `Seq` demo (complement, reverse complement, transcription, translation).

## Version Compatibility

- Python ≥ 3.10 (f-strings, `str.maketrans`, walrus operator all stable)
- biopython ≥ 1.83 (`Bio.SeqUtils.gc_fraction`; older code used the removed `GC()` function)
- pandas ≥ 2.1, numpy ≥ 1.26 (only needed once you move past raw strings to tabular data)

## Prerequisites

- No prior programming experience required for this skill itself.
- `pip install biopython` (or `conda install -c bioconda biopython`) for the `Bio.Seq` demo.
- A terminal for `venv`/`conda` environment setup.

## Goal: Count nucleotides and compute GC content from a raw DNA string

**Approach:** Use `str.count()` for a one-off tally, or a loop over a `dict` when you need per-base percentages in one pass. GC content (fraction of G+C) is one of the first statistics computed on any new sequence — it correlates with melting temperature and gene density.

```python
def nucleotide_counts(dna: str) -> dict:
    """Return counts of A/C/G/T in a DNA string using str.count()."""
    dna = dna.upper()
    return {base: dna.count(base) for base in "ACGT"}


def nucleotide_percentages(dna: str) -> dict:
    """Return per-base percentages via a single pass through the sequence."""
    counts = {"A": 0, "C": 0, "G": 0, "T": 0}
    for nt in dna.upper():
        if nt in counts:
            counts[nt] += 1
    total = len(dna)
    return {base: count / total * 100 for base, count in counts.items()}


def gc_content(sequence: str) -> float:
    """Calculate GC content of a DNA sequence as a percentage (0-100)."""
    sequence = sequence.upper()
    gc = sequence.count("G") + sequence.count("C")
    return gc / len(sequence) * 100


if __name__ == "__main__":
    # From Rosalind "Counting DNA Nucleotides"
    dna = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC"

    counts = nucleotide_counts(dna)
    print(f"Counts: {counts}  (total={sum(counts.values())}, len={len(dna)})")

    gc = gc_content(dna)
    print(f"GC content: {gc:.2f}%")
    if gc < 40:
        print("AT-rich (low GC).")
    elif gc > 60:
        print("GC-rich.")
    else:
        print("Moderate GC range.")
```

## Goal: Basic sequence manipulation with BioPython's `Seq`

**Approach:** `Bio.Seq.Seq` wraps a string with biology-aware methods (`complement`, `reverse_complement`, `transcribe`, `translate`) so you don't hand-roll base-pairing tables for routine tasks.

```python
def biopython_demo(sequence: str = "ATGAAACCCGGGTAA") -> None:
    """Show core Bio.Seq operations: complement, RC, transcription, translation, GC."""
    from Bio.Seq import Seq
    from Bio.SeqUtils import gc_fraction

    seq = Seq(sequence)
    print(f"Sequence:           {seq}")
    print(f"Complement:         {seq.complement()}")
    print(f"Reverse complement: {seq.reverse_complement()}")
    print(f"Transcribed (RNA):  {seq.transcribe()}")
    print(f"Translated:         {seq.translate()}")
    print(f"GC fraction:        {gc_fraction(seq):.3f}")


if __name__ == "__main__":
    biopython_demo()
```

## Goal: Set up an isolated environment for a bioinformatics project

**Approach:** Use `venv` for a pure-Python project; use `conda` when you also need compiled, non-Python tools (BLAST, samtools) in the same environment.

```bash
# --- pip / venv ---
python -m venv bioenv
source bioenv/bin/activate        # macOS/Linux; use bioenv\Scripts\activate on Windows
pip install biopython pandas numpy matplotlib seaborn
pip install -r requirements.txt   # if you have one
pip list
deactivate

# --- conda (preferred when you also need CLI tools like blast/samtools) ---
conda create -n bioenv python=3.11
conda activate bioenv
conda install -c conda-forge biopython pandas matplotlib
conda install -c bioconda blast samtools
```

## Pitfalls

- **f-strings vs `%` formatting:** f-strings (`f"GC = {gc:.2f}%"`) are the modern approach (Python ≥3.6); older code uses `"GC = %.2f%%" % gc`.
- **`print()` returns `None`:** never write `result = print(...)` expecting to capture output — assign the value directly instead.
- **Mutable default arguments:** never `def f(x=[])`; use `def f(x=None): x = x if x is not None else []`.
- **0-based vs 1-based coordinates:** Python slicing/ranges are half-open `[start, stop)` and 0-based, but genomic coordinate systems (GFF, VCF, 1-based) are not — off-by-one errors are the most common bug when converting between them.
- **Deep vs shallow copy:** nested structures (e.g. a list of per-sample count dicts) need `copy.deepcopy()`; `list.copy()` only copies the top level.
- **`Bio.SeqUtils.GC()` is gone:** newer BioPython uses `gc_fraction()` (returns 0-1, not 0-100) — multiply by 100 if you want a percentage.

## See Also

- `biopython` — deeper Bio.Seq/Bio.SeqIO/Bio.SeqUtils reference
- `bio-sequence-io-read-sequences` — reading FASTA/FASTQ files instead of raw strings
- `bio-sequence-manipulation-seq-objects` — Seq object operations in depth
- `bio-sequence-manipulation-transcription-translation` — transcription/translation details

