Project 1 - Protein Stability
Drug Design with PyRosetta — Alanine Scanning of Lysozyme
The Big Picture
These 6 cells together tell one scientific story:
Load a real protein → Measure its stability → Change amino acids → See which changes make the protein weaker or stronger → Plot the results
This is exactly the kind of analysis researchers use to study drug resistance.
Cell 1 — Start PyRosetta
Import and initialize the protein modeling engine
import pyrosetta
pyrosetta.init()
print("PyRosetta ready!")
This cell has two important jobs:
-
import pyrosetta— Loads the PyRosetta library into Python. Think of a library as a toolbox full of pre-written tools that someone else built for you. -
pyrosetta.init()— Starts up all the internal machinery Rosetta needs: databases, energy scoring parameters, and chemical definitions for all 20 amino acids.
Analogy: This is like turning on the engine of a car before you drive. Nothing else will work if you skip this step.
Key Concepts:
- Library: A collection of pre-written Python code that adds new capabilities to your program.
- import: The Python command that loads a library so you can use its tools.
- init(): Short for “initialize” — sets everything up and gets it ready to use.
Cell 2 — Load a Protein
Download a real protein structure from the global database
from pyrosetta.toolbox import pose_from_rcsb
# Download lysozyme from the Protein Data Bank
pose = pose_from_rcsb("1LYZ")
print(f"Protein loaded: {pose.total_residue()} amino acids")
This cell downloads a real protein structure from the internet and loads it into memory.
-
pose_from_rcsb— A tool that knows how to talk to the Protein Data Bank website. -
"1LYZ"— The PDB ID code for Lysozyme, a protein found in egg whites and human tears. Every protein structure deposited in the database has a unique 4-character ID. -
pose— A variable that stores the entire protein structure: every atom, every bond, every 3D coordinate.
Analogy: The Protein Data Bank is like a global library of protein blueprints. “1LYZ” is the catalogue number for Lysozyme’s blueprint.
pose_from_rcsbgoes to that library and downloads the blueprint for us.
Key Concepts:
- Protein Data Bank (PDB): A worldwide database containing over 200,000 experimentally determined protein structures, freely available to everyone.
- Pose: Rosetta’s internal representation of a protein — stores all atomic coordinates, chemistry, and sequence information.
- Variable: A named container in Python that stores a value. Here, “pose” stores the entire protein.
Cell 3 — Score the Original Protein
Calculate a baseline stability measurement
# Create a scoring function (like a stability calculator)
scorefxn = pyrosetta.get_fa_scorefxn()
# Score the original protein
original_score = scorefxn(pose)
print(f"Original stability score: {original_score:.2f}")
print("(Lower score = more stable protein)")
This cell creates a scoring function and uses it to measure how stable the protein is.
-
get_fa_scorefxn()— “fa” stands for Full Atom. This creates Rosetta’s standard energy scoring function, which calculates the protein’s stability by examining every single atom and its interactions with neighbouring atoms. -
scorefxn(pose)— Applies the scoring function to the protein. It adds up many different physical forces: van der Waals interactions, hydrogen bonds, electrostatics, and more. -
original_score— Saved as the baseline. All future mutant scores will be compared to this number. The unit is REU (Rosetta Energy Units).
Analogy: Think of the scoring function as a building inspector who checks every part of a structure and gives it a score. Lower scores mean the building (protein) is better constructed and more stable.
Key Concepts:
- Scoring function: A mathematical formula that estimates a protein’s free energy. Based on physics and statistics from known protein structures.
- REU: Rosetta Energy Units — the unit used for Rosetta scores. Lower (more negative) = more stable.
- Baseline: The original measurement we compare everything else to. Like a control group in a science experiment.
Cell 4 — Mutate One Amino Acid
Change a single residue and measure the effect
from pyrosetta.toolbox import mutate_residue
# Make a copy of the original protein
mutant_pose = pose.clone()
# Change amino acid at position 50 to Alanine
mutate_residue(mutant_pose, 50, "A")
# Score the mutant
mutant_score = scorefxn(mutant_pose)
difference = mutant_score - original_score
print(f"Original score: {original_score:.2f}")
print(f"Mutant score: {mutant_score:.2f}")
print(f"Difference: {difference:.2f}")
if difference > 0:
print("This mutation DESTABILIZES the protein")
else:
print("This mutation STABILIZES the protein")
This is the core of the experiment — we swap one amino acid and measure the impact on stability.
-
pose.clone()— Makes an exact copy of the original protein. We never modify the original directly, so we always have our baseline to compare against. -
mutate_residue(mutant_pose, 50, "A")— Swaps the amino acid at position 50 for Alanine. “A” is the single-letter code for Alanine, the simplest amino acid. We use Alanine because it removes the side chain while keeping the protein backbone intact. -
difference = mutant_score - original_score— Calculates how much the mutation changed the stability.
| Difference | Meaning | Real-world example |
|---|---|---|
| Positive (e.g. +5.2) | Destabilizing — protein is weaker | Drug resistance mutations often destabilize the drug binding site |
| Negative (e.g. -3.1) | Stabilizing — protein is stronger | Protein engineers use stabilizing mutations to make better medicines |
Analogy: This is like replacing one brick in a wall and checking if the wall becomes stronger or weaker. Drug-resistant bacteria do exactly this — a single amino acid change can make a protein ignore an antibiotic completely.
Key Concepts:
- Mutation: A change in the amino acid sequence of a protein. Can be stabilizing, destabilizing, or neutral.
- Alanine scanning: A technique where each amino acid is replaced with Alanine one at a time to identify which residues are most important.
- clone(): Creates an independent copy of the protein so the original is not modified.
Cell 5 — Test Multiple Mutations
Automate the experiment across many positions
import pandas as pd
results = []
residues_to_test = [20, 30, 40, 50, 60, 70, 80]
for res_num in residues_to_test:
mutant = pose.clone()
original_aa = pose.residue(res_num).name1()
mutate_residue(mutant, res_num, "A")
score = scorefxn(mutant)
diff = score - original_score
results.append({
"Residue": res_num,
"Original AA": original_aa,
"Score Change": round(diff, 2),
"Effect": "Destabilizing" if diff > 0 else "Stabilizing"
})
df = pd.DataFrame(results)
print(df.to_string(index=False))
This cell automates Cell 4 — instead of doing one mutation at a time, it tests 7 positions automatically and collects all the results into a table.
-
import pandas as pd— Pandas is a Python library for working with tables of data. “pd” is just a shorthand nickname. -
results = []— Creates an empty list where we will collect all our results. -
for res_num in residues_to_test:— A loop that repeats the indented block of code once for each residue number in the list. Each time through,res_numtakes the next value. -
pose.residue(res_num).name1()— Looks up what amino acid is currently at that position and returns its single-letter code (e.g. “G” for Glycine). -
pd.DataFrame(results)— Converts the list of results into a proper table with labelled columns, like a spreadsheet.
Analogy: Instead of manually testing each brick in a wall one by one, this is like having a robot test all 7 bricks automatically and write down the results in a table for you.
Key Concepts:
- Loop (for): A programming construct that repeats a block of code multiple times, once for each item in a list.
- List: An ordered collection of values in Python, written with square brackets:
[20, 30, 40, ...] - DataFrame: A pandas table with rows and columns, similar to an Excel spreadsheet, that makes data easy to analyze.
- append(): Adds a new item to the end of a list.
Cell 6 — Plot Your Results
Visualize the data as a colour-coded bar chart
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 5))
colors = ["red" if x > 0 else "green" for x in df["Score Change"]]
plt.bar(df["Residue"].astype(str), df["Score Change"], color=colors)
plt.axhline(y=0, color="black", linestyle="-", linewidth=0.8)
plt.xlabel("Residue Position")
plt.ylabel("Score Change (REU)")
plt.title("Effect of Alanine Mutations on Lysozyme Stability")
plt.legend(handles=[
plt.Rectangle((0,0),1,1, color="red", label="Destabilizing"),
plt.Rectangle((0,0),1,1, color="green", label="Stabilizing")
])
plt.tight_layout()
plt.savefig("stability_results.png")
plt.show()
This cell turns the table of numbers from Cell 5 into a visual bar chart that is easy to read and share.
-
import matplotlib.pyplot as plt— Matplotlib is Python’s main plotting library. “plt” is its standard nickname. -
figsize=(10, 5)— Sets the chart size to 10 inches wide and 5 inches tall. -
"red" if x > 0 else "green"— A list comprehension that assigns a colour to each bar: red for destabilizing, green for stabilizing. -
plt.axhline(y=0)— Draws a horizontal line at zero so it is easy to see which bars go above (bad) and below (good) the baseline. -
plt.savefig("stability_results.png")— Saves the chart as an image file so you can include it in a report or presentation.
Analogy: Instead of reading a table of numbers, a bar chart lets your brain instantly see the pattern — which positions are critical and which are less important. Scientists always visualize data because humans are much better at reading pictures than tables of numbers.
Key Concepts:
- matplotlib: Python’s most popular library for creating charts and graphs.
- Bar chart: A chart where each category is represented by a bar whose height shows its value. Great for comparing results across multiple conditions.
- List comprehension: A compact Python syntax for creating a list by applying a rule to every item:
["red" if x > 0 else "green" for x in scores] - savefig(): Saves the current chart as an image file (PNG, JPG, PDF, etc.)