01 Why Python for biology?
If you work in bioinformatics, you will write Python. Not because someone decided this arbitrarily — but because Python became the dominant language of this field for very concrete reasons. Every major analysis tool — Snakemake, Scanpy, MACS3, DeepVariant — is written in Python. The libraries for handling biological data are mature and actively maintained. And the syntax is readable enough that a biologist, not a computer scientist, can learn it productively.
Bash handles pipelines, file operations, and process management on the command line. Python handles everything more complex: parsing file formats, computing statistics on sequences, building reusable functions, querying databases, and producing publication-quality figures. The two languages work together — Bash orchestrates the pipeline, Python does the analysis inside each step.
You receive 48 RNA-seq FASTQ files. A Bash script loops over them and calls STAR to align each one. The counts from STAR go into a folder. A Python script then reads all count files, builds a gene-by-sample matrix, filters low-count genes, and writes a clean TSV that DESeq2 will read in R. That Python script is always custom — written by a bioinformatician for that specific project. That is the script you will be able to write after this module.
A second example: Sorghum bicolor has a GC content of ~61%. When you sequence a sample and suspect contamination, you write a Python script that reads each FASTQ read, computes GC content, and flags reads that deviate more than 15% from the expected value. Ten lines of Python. This lesson teaches you the foundation for exactly that.
Python versions
Python 2 is dead and removed from all major Linux distributions. Every bioinformatics tool you will encounter requires Python 3. The current stable version is Python 3.12. This course uses 3.10 or higher throughout. Never install system-wide packages — always use a conda environment.
02 Setting up with conda
The first rule of bioinformatics Python: never install packages into system Python. The Python that ships with Ubuntu is owned by the operating system. Installing packages into it can break system tools. Instead, create a dedicated conda environment called bioinfo-py — an isolated Python installation with its own packages, completely separate from the system.
Imagine two projects running in parallel. Project A needs Biopython 1.79 and numpy 1.23. Project B needs Biopython 1.83 and numpy 1.26. If they share one Python installation, the packages conflict and one project breaks. Conda environments solve this by giving each project its own isolated Python with its own dependency versions.
In bioinformatics this is not optional — tools like MACS3, Scanpy, and pysam each have strict and often conflicting dependency trees. Environments are how professionals stay sane. You will create a new environment for each major analysis in your career.
Create the bioinfo-py environment
# Create a fresh conda environment named "bioinfo-py" # with Python 3.12 — the current stable release conda create -n bioinfo-py python=3.12 -y # Activate it — your prompt will change to (bioinfo-py) conda activate bioinfo-py # Confirm you are using the environment's Python, not the system one which python /home/shajedur/miniforge3/envs/bioinfo-py/bin/python python --version Python 3.12.x
which python must contain your environment name (envs/bioinfo-py). If it shows /usr/bin/python, the environment is not active. Run conda activate bioinfo-py again.
Create your working directory
All scripts and notebooks for this course live in one folder. Create it now and keep it organised from lesson 1.
# Create the full directory structure for the course mkdir -p ~/python-bioinfo-scripts/notebooks mkdir -p ~/python-bioinfo-scripts/scripts mkdir -p ~/python-bioinfo-scripts/data/raw mkdir -p ~/python-bioinfo-scripts/data/processed mkdir -p ~/python-bioinfo-scripts/results # Confirm the structure was created ls -lh ~/python-bioinfo-scripts/ drwxr-xr-x notebooks/ drwxr-xr-x scripts/ drwxr-xr-x data/ drwxr-xr-x results/
03 Essential packages
Python's power in bioinformatics comes from its ecosystem. Five packages cover the vast majority of what you will do in this course and in a real lab:
numpy gives you fast numerical arrays — essential when you have a matrix of 20,000 genes × 500 samples and need to compute means, standard deviations, or correlations across all of them in milliseconds. Plain Python lists are too slow for this.
pandas gives you DataFrames — the table structure you will use to hold count matrices, VCF metadata, sample annotation files, and any tabular output from alignment tools. Think of it as a programmable spreadsheet.
biopython understands biological file formats natively — FASTA, FASTQ, GenBank, BLAST XML, VCF — so you do not have to write parsers from scratch. It also wraps common operations: translation, reverse complement, sequence alignment scoring.
matplotlib and seaborn produce the plots that go into papers: coverage plots, heatmaps, volcano plots, PCA scatter plots. Seaborn is built on top of matplotlib and adds statistical visualisations with less code.
# Activate your environment (if not already active) conda activate bioinfo-py # Install all core packages in one command # -c conda-forge : the community-maintained package channel (more up-to-date) # -c bioconda : the biology-specific package channel (biopython lives here) # -y : auto-confirm without prompts conda install -c conda-forge -c bioconda -y \ numpy pandas biopython matplotlib seaborn jupyterlab ipykernel # Verify every package installed correctly # If any line throws an error, re-run the install above python -c "import numpy, pandas, Bio, matplotlib, seaborn; print('All packages OK')" All packages OK
| Package | What it does | Used in lesson |
|---|---|---|
| numpy | Fast numerical arrays and maths | Lesson 5 |
| pandas | DataFrames for tabular genomic data | Lesson 5 |
| biopython | Biological file format parsers | Lesson 6 |
| matplotlib | Core plotting library | Lesson 8 |
| seaborn | Statistical visualisation | Lesson 8 |
| jupyterlab | Interactive notebook environment | This lesson |
| ipykernel | Registers your conda env as a Jupyter kernel | This lesson |
sudo pip install for bioinformatics packages. Always install inside an active conda environment. If your prompt shows (base) instead of (bioinfo-py), your packages will go to the wrong place.
04 Jupyter notebooks
A Jupyter notebook is an interactive document that mixes code, output, and explanation in a single file. You write Python in cells, run each cell individually, and see the result immediately below — including plots, tables, and printed output. The file is saved as .ipynb (IPython Notebook).
In a traditional script (.py file), you run the whole thing at once. When it crashes on line 200, you restart from scratch. In a notebook, you run cells one at a time. You can load a 2 GB FASTQ file into memory in cell 1, then experiment with different filtering approaches in cells 2–10, without reloading the data each time. This iterative style matches how biology research actually works — you explore first, then formalise.
Most bioinformatics publications and tutorials share their analysis as Jupyter notebooks. GitHub renders them directly in the browser. When you push your notebooks to GitHub, anyone can read your code and see your results without running anything. This is why notebooks are the standard format for reproducible bioinformatics analysis.
That said: notebooks are for exploration; scripts are for pipelines. In production, you convert a finished notebook into a clean .py script. Lesson 9 covers this transition.
Register your conda environment as a Jupyter kernel
By default, JupyterLab uses its own built-in Python. You need to register your bioinfo-py environment so Jupyter knows about it and can run code inside it.
# Make sure bioinfo-py is active conda activate bioinfo-py # Register the environment as a Jupyter kernel # --user : installs for your user only (no sudo needed) # --name : the internal kernel name # --display-name : what you see in the Jupyter kernel selector python -m ipykernel install \ --user \ --name bioinfo-py \ --display-name "Python (bioinfo-py)" Installed kernelspec bioinfo-py in /home/shajedur/.local/share/jupyter/kernels/bioinfo-py # Confirm the kernel was registered jupyter kernelspec list Available kernels: python3 /home/shajedur/miniforge3/share/jupyter/kernels/python3 bioinfo-py /home/shajedur/.local/share/jupyter/kernels/bioinfo-py
Launch JupyterLab
# Navigate to your course folder first # (JupyterLab opens with this folder as its root) cd ~/python-bioinfo-scripts # Launch JupyterLab # This starts a local server and opens a browser tab automatically jupyter lab # You will see output like: # [I] Serving notebooks from local directory: /home/shajedur/python-bioinfo-scripts # [I] Jupyter Server is running at: http://localhost:8888/lab?token=abc123... # A browser tab opens automatically. If it doesn't: copy the URL into Firefox.
.ipynb files in whichever folder you launched from. Never close the terminal while Jupyter is running — it will shut down.
Select the right kernel
When you create a new notebook in JupyterLab, you will be asked to select a kernel. Always choose "Python (bioinfo-py)" — the one you just registered. This ensures the notebook has access to numpy, pandas, biopython, and all the other packages you installed. If you accidentally use the wrong kernel, click the kernel name in the top-right corner of the notebook to switch it.
05 Your first notebook
Create a new notebook in JupyterLab: File → New → Notebook, then select the Python (bioinfo-py) kernel. Save it as notebooks/lesson-01-setup.ipynb. Now type the following cells — one cell at a time, running each with Shift+Enter.
Cell 1 — Verify packages and print versions
# Cell 1: confirm every package is available and print its version import numpy as np import pandas as pd import Bio import matplotlib import seaborn print(f"numpy : {np.__version__}") print(f"pandas : {pd.__version__}") print(f"biopython : {Bio.__version__}") print(f"matplotlib : {matplotlib.__version__}") print(f"seaborn : {seaborn.__version__}") print("\\n✓ All packages loaded successfully.")
Expected output after pressing Shift+Enter:
numpy : 1.26.x
pandas : 2.x.x
biopython : 1.83
matplotlib : 3.8.x
seaborn : 0.13.x
✓ All packages loaded successfully.
Cell 2 — Your first sequence analysis in a notebook
GC content is the percentage of bases in a DNA sequence that are Guanine or Cytosine. It matters in three practical contexts: (1) PCR primer design — primers need 40–60% GC to bind at the right temperature; (2) quality control — reads with unusual GC content relative to the reference genome signal bias or contamination; (3) species identity — Sorghum bicolor has ~61% GC content, so a metagenomic sample with very different GC distribution warrants investigation.
# Cell 2: analyse a DNA sequence — length, GC content, reverse complement # A real Sorghum bicolor gene fragment (SbDREB2 transcription factor) sequence = "ATGGCGAGCGACGAGCTGCAGCAGCTGCAGCAGCAGATGCAGCAGCAGCAGCCGCCGCCG" # 1. Length length = len(sequence) # 2. GC content gc = (sequence.count("G") + sequence.count("C")) / length * 100 # 3. Reverse complement # str.maketrans creates a character-by-character substitution table # A→T, T→A, G→C, C→G # Then [::-1] reverses the complemented string comp_map = str.maketrans("ATGC", "TACG") rev_comp = sequence.translate(comp_map)[::-1] # 4. Print report print(f"Sequence : {sequence}") print(f"Length : {length} bp") print(f"GC content : {gc:.1f}%") print(f"RevComp : {rev_comp}")
Sequence : ATGGCGAGCGACGAGCTGCAGCAGCTGCAGCAGCAGATGCAGCAGCAGCAGCCGCCGCCG
Length : 60 bp
GC content : 68.3%
RevComp : CGGCGGCGGCTGCTGCTGCTGCTGTGCTGCTGCTGCAGCTGCTGCTCGTCGCTGCCAT
Cell 3 — Your first plot
# Cell 3: plot base composition as a bar chart # %matplotlib inline makes plots appear inside the notebook import matplotlib.pyplot as plt bases = ["A", "T", "G", "C"] counts = [sequence.count(b) for b in bases] colors = ["#e8b72a", "#f42a41", "#006a4e", "#3b82f6"] fig, ax = plt.subplots(figsize=(6, 4)) ax.bar(bases, counts, color=colors, edgecolor="white", linewidth=1.5) ax.set_title("Base composition — SbDREB2 fragment", fontsize=13, fontweight="bold") ax.set_xlabel("Base") ax.set_ylabel("Count") ax.spines["top"].set_visible(False) ax.spines["right"].set_visible(False) plt.tight_layout() plt.show()
Running this cell produces a bar chart showing the count of each base — directly inside the notebook, below the cell. This is the core workflow of exploratory bioinformatics analysis.
06 Quick reference
| Task | Command | Notes |
|---|---|---|
| Create environment | conda create -n bioinfo-py python=3.12 -y | One time setup |
| Activate environment | conda activate bioinfo-py | Every new terminal session |
| Check active Python | which python | Must show envs/bioinfo-py path |
| Install packages | conda install -c conda-forge -c bioconda numpy pandas biopython matplotlib seaborn jupyterlab | Inside active env only |
| Register kernel | python -m ipykernel install --user --name bioinfo-py --display-name "Python (bioinfo-py)" | One time |
| List kernels | jupyter kernelspec list | Verify registration |
| Launch JupyterLab | jupyter lab | Run from course folder |
| Stop JupyterLab | Ctrl+C twice in terminal | Save notebook first |
| Run notebook cell | Shift+Enter | Runs cell and moves to next |
| Check package version | python -c "import numpy; print(numpy.__version__)" | From terminal |
07 Exercises
Complete all four exercises inside JupyterLab. Create a single notebook called notebooks/lesson-01-exercises.ipynb and add each exercise as a new cell. Run each cell with Shift+Enter before moving on.
Activate the bioinfo-py environment in your terminal, launch JupyterLab, create a new notebook, select the Python (bioinfo-py) kernel, and print the version of all five packages in one cell. Every version should print without error.
▶ Show answer
import numpy, pandas, Bio, matplotlib, seaborn print(f"numpy {numpy.__version__}") print(f"pandas {pandas.__version__}") print(f"biopython {Bio.__version__}") print(f"matplotlib {matplotlib.__version__}") print(f"seaborn {seaborn.__version__}")
Copy the GC content code from Cell 2 above. Replace the sequence with this Zea mays (maize) Ubiquitin promoter fragment and rerun. What is the GC content? Is it higher or lower than the sorghum fragment?
AGATCTTGCGCGCTATAAATACGCGCTATATATAGCGCGCGCGATATATATCGCGCGATAT
▶ Show answer
sequence = "AGATCTTGCGCGCTATAAATACGCGCTATATATAGCGCGCGCGATATATATCGCGCGATAT" gc = (sequence.count("G") + sequence.count("C")) / len(sequence) * 100 print(f"GC content: {gc:.1f}%") # ~43% — AT-rich compared to the sorghum fragment (68%) # Ubiquitin promoter regions are often AT-rich in grasses
In a new cell, write a function called gc_content(seq) that takes any DNA string and returns the GC percentage as a float. Test it on three sequences of your choice and print the results.
def function_name(parameter):. The function body is indented. Use return to send a value back to the caller.
▶ Show answer
def gc_content(seq): """Return GC content as a percentage (float).""" seq = seq.upper() return (seq.count("G") + seq.count("C")) / len(seq) * 100 print(gc_content("ATATATATAT")) # 20.0 — AT-rich print(gc_content("ATGCATGCAT")) # 50.0 — balanced print(gc_content("GCGCGCGCGC")) # 100.0 — GC-rich
Create a Python list containing five different DNA sequences of your choice (at least 20 bp each). Loop over the list, call your gc_content() function on each, and print whether each sequence is AT-rich (<50%), balanced (50–60%), or GC-rich (>60%). Print the sequence number, GC%, and category on one line.
▶ Show answer
sequences = [
"ATATATATATATATATAT",
"ATGCATGCATGCATGCAT",
"GCGCGCGCGCGCGCGCGC",
"ATGGCGAGCGACGAGCTG",
"AGATCTTGCGCGCTATAA",
]
for i, seq in enumerate(sequences, 1):
gc = gc_content(seq)
if gc < 50: cat = "AT-rich"
elif gc <= 60: cat = "Balanced"
else: cat = "GC-rich"
print(f"Seq {i}: {gc:.1f}% → {cat}")
Your progress saves automatically in this browser.