Foundations & Prerequisites•2026-09-22

Python Fundamentals for Bioinformatics

NM

Nasir Mahmood Abbasi, PhD

Bioinformatics Educator

Python Fundamentals for Bioinformatics

Python becomes useful in bioinformatics when it turns repeated manual inspection into a testable program. This lesson teaches a small, practical subset of Python and applies it to sequence records and tabular data.

Tested on: Python 3.11, R 4.3.2, Ubuntu 24.04
Last Review: 2026-09-01

Learning Objectives & Prerequisites

By the end of this lesson, you should be able to:

  • Use variables, lists, dictionaries, loops, functions, and conditions.
  • Read text files safely and count simple sequence statistics.
  • Use a virtual environment and record package versions.
  • Write a small script with a clear input and output.

Prerequisites:

Expected Output

By the end of this lesson, you should have: A small reproducible Python script or notebook that reads a biological table, performs one transparent transformation, and writes a named output file.

1. Values and collections

Use strings for sequences, lists for ordered records, and dictionaries for keyed metadata.

sequence = "ACGTACGT"
length = len(sequence)
gc = (sequence.count("G") + sequence.count("C")) / length
print(f"length={length}, gc={gc:.2%}")

2. Functions and validation

Functions make repeated operations testable. Validate input before calculating a result.

def gc_fraction(sequence):
    seq = sequence.strip().upper()
    if not seq or any(base not in "ACGTN" for base in seq):
        raise ValueError("Expected a DNA sequence")
    return (seq.count("G") + seq.count("C")) / len(seq)

3. Read a small FASTA file

For production work use a tested parser such as Biopython, but a simple parser is useful for understanding the format.

def fasta_records(path):
    name, seq = None, []
    with open(path) as handle:
        for line in handle:
            line=line.strip()
            if line.startswith(">"):
                if name is not None: yield name, "".join(seq)
                name, seq = line[1:], []
            else: seq.append(line)
        if name is not None: yield name, "".join(seq)

4. Environments and tests

Keep project dependencies isolated and test a function with a known sequence.

python -m venv .venv
source .venv/bin/activate
python -m pip install biopython
python -c "import Bio; print(Bio.__version__)"

5. Environment Setup and Virtual Environments

Never install packages into the system Python. Always use a virtual environment or conda to isolate dependencies per project.

# Create a virtual environment
python3 -m venv envs/bioinfo
source envs/bioinfo/bin/activate

# Install core bioinformatics libraries
pip install biopython pandas matplotlib numpy

# Verify installation
python -c "import Bio; print(Bio.__version__)"
# Expected: 1.83 (or your installed version)

# Freeze exact versions for reproducibility
pip freeze > envs/requirements.txt

6. Parsing Biological Files with Biopython

The most common Python task in bioinformatics is reading and writing sequence files. Biopython's SeqIO module handles FASTA, FASTQ, GenBank, and many other formats.

from Bio import SeqIO

# Parse a FASTA file and extract basic statistics
records = list(SeqIO.parse("data/raw/sequences.fasta", "fasta"))
print(f"Total sequences: {len(records)}")

for record in records[:3]:
    print(f"  {record.id}: {len(record.seq)} bp, GC={gc_fraction(record.seq):.2%}")

def gc_fraction(seq):
    """Calculate GC content of a DNA sequence."""
    seq = seq.upper()
    gc = sum(1 for base in seq if base in "GC")
    return gc / len(seq) if len(seq) > 0 else 0.0

# Expected output:
# Total sequences: 1542
#   seq_001: 2341 bp, GC=42.15%
#   seq_002: 1876 bp, GC=38.90%
#   seq_003: 3102 bp, GC=45.22%

7. Working with Tabular Data Using Pandas

Most bioinformatics workflows produce tabular data (count matrices, variant tables, metadata). Pandas is the standard tool for manipulating these in Python.

import pandas as pd

# Read a gene expression count matrix
counts = pd.read_csv("data/raw/gene_counts.tsv", sep="\t", index_col=0)
print(f"Shape: {counts.shape}")  # (genes, samples)
print(counts.head())

# Filter genes with low total counts
min_total = 10
filtered = counts[counts.sum(axis=1) >= min_total]
print(f"Genes before filtering: {counts.shape[0]}")
print(f"Genes after filtering: {filtered.shape[0]}")

# Calculate basic statistics per sample
sample_stats = pd.DataFrame({
    "total_reads": counts.sum(),
    "detected_genes": (counts > 0).sum(),
    "median_count": counts.median()
})
print(sample_stats)

8. Writing Functions and Modular Code

As your analyses grow, organize reusable logic into functions with type hints, docstrings, and input validation.

from pathlib import Path

def load_count_matrix(filepath: str, min_total: int = 10) -> pd.DataFrame:
    """Load and filter a gene count matrix.

    Args:
        filepath: Path to tab-separated count matrix.
        min_total: Minimum total counts across all samples.

    Returns:
        Filtered DataFrame with genes as rows, samples as columns.

    Raises:
        FileNotFoundError: If the input file does not exist.
        ValueError: If the matrix is empty after filtering.
    """
    path = Path(filepath)
    if not path.exists():
        raise FileNotFoundError(f"Count matrix not found: {path}")

    df = pd.read_csv(path, sep="\t", index_col=0)
    filtered = df[df.sum(axis=1) >= min_total]

    if filtered.empty:
        raise ValueError(f"No genes passed the min_total={min_total} filter")

    return filtered

9. Testing and Validation

Always validate your outputs before proceeding to the next analysis step:

# Assertions catch silent errors early
assert counts.shape[0] > 0, "Count matrix is empty"
assert not counts.isnull().any().any(), "NaN values detected in count matrix"
assert (counts >= 0).all().all(), "Negative counts detected"

# Check for duplicate gene names
duplicates = counts.index[counts.index.duplicated()]
if len(duplicates) > 0:
    print(f"WARNING: {len(duplicates)} duplicate gene IDs found")
Problem Likely Cause Solution
ModuleNotFoundError Package not installed in active environment Activate your venv and pip install the package
FileNotFoundError Wrong working directory or relative path Use Path(__file__).parent or absolute paths
UnicodeDecodeError Binary file opened as text Use "rb" mode or check file format
Pandas silently drops rows Duplicate index values Use reset_index() or deduplicate explicitly

Practical Exercise

Write fasta_gc.py that prints each FASTA identifier, sequence length, and GC percentage. Test it on two records, including one containing N.

Pass criteria: The script validates input, prints deterministic output for both records, and exits with a useful error for invalid characters.

Troubleshooting

If imports fail, confirm the virtual environment is active. If a file is not found, print the current directory and use an explicit relative or absolute path.

Knowledge Check & Assessment

1. Concept Verification

Write short answers explaining the main concepts, the assumptions behind them, and one way a careless workflow could produce a misleading result.

2. Practical Execution

Complete the practical exercise above and save the command, script, table, or figure in the project structure. Pass Criteria: The script validates input, prints deterministic output for both records, and exits with a useful error for invalid characters.

3. Troubleshooting

Explain what you would inspect first if the output were empty, malformed, unexpectedly large, or failed because of a missing file, package, permission, memory, or metadata problem.

Next Steps

Continue with R and Tidyverse Fundamentals and Biological Data Formats. Record the software versions, dataset or example inputs, and any decisions you made.

Reviewed: February 2026

All commands and outputs were verified with the software versions listed in this tutorial. If you encounter reproducibility issues, please report them through the Contact page.

Author: Nasir Mahmood Abbasi, PhD · Category: Foundations & Prerequisites

Continue Learning

Course Sequence