Generate a walker series¶
A "walker" is a common technique in oligonucleotide design where you systematically modify one position at a time across a strand. This is useful for:
- Identifying optimal modification positions
- Generating a library of variants
- Testing the effect of modifications at each position
This example demonstrates a UNA walk - replacing each sugar position with UNA one at a time.
Setup¶
HELMshaker bundles no monomers, so every example needs a monomer library.
These docs use a small public HELMCore subset committed at
docs/examples/docs_library.json. In your own code, pull a dictionary from
TMR instead. See Load a monomer library.
In [1]:
Copied!
from helmshaker import Molecule, MonomerLibrary
library = MonomerLibrary()
library.load_from_file("../examples/docs_library.json")
print(f"{len(library)} monomers available")
from helmshaker import Molecule, MonomerLibrary
library = MonomerLibrary()
library.load_from_file("../examples/docs_library.json")
print(f"{len(library)} monomers available")
40 monomers available
Setup: Load the Parent Molecule¶
In [2]:
Copied!
from helmshaker import Molecule
parent_helm = "CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].[fl2r](C)[sp].[m](U)p.[fl2r](A)p.[m](A)p.[fl2r](U)p.[m](C)p.[fl2r](U)p.[m](C)p.[fl2r](C)p.[m](A)p.[fl2r](C)p.[m](U)p.[fl2r](U)p.[m](C)p.[fl2r](A)p.[m](U)p.[fl2r](C)p.[m](C)[sp].[m](U)[sp].[m](U)}|RNA2{[m](G)[sp].[m](G)[sp].[fl2r](A)p.[m](U)p.[fl2r](G)p.[m](A)p.[fl2r](A)p.[m](G)p.[fl2r](U)p.[m](G)p.[fl2r](G)p.[m](A)p.[fl2r](G)p.[m](A)p.[fl2r](U)p.[m](U)p.[fl2r](A)p.[m](G)[sp].[fl2r](U)[sp]}$RNA2,CHEM1,57:R2-1:R1|CHEM1,CHEM2,1:R2-1:R1|RNA2,RNA1,11:pair-47:pair|RNA2,RNA1,14:pair-44:pair|RNA2,RNA1,17:pair-41:pair|RNA2,RNA1,20:pair-38:pair|RNA2,RNA1,23:pair-35:pair|RNA2,RNA1,26:pair-32:pair|RNA2,RNA1,29:pair-29:pair|RNA2,RNA1,2:pair-56:pair|RNA2,RNA1,32:pair-26:pair|RNA2,RNA1,35:pair-23:pair|RNA2,RNA1,38:pair-20:pair|RNA2,RNA1,41:pair-17:pair|RNA2,RNA1,44:pair-14:pair|RNA2,RNA1,47:pair-11:pair|RNA2,RNA1,50:pair-8:pair|RNA2,RNA1,53:pair-5:pair|RNA2,RNA1,56:pair-2:pair|RNA2,RNA1,5:pair-53:pair|RNA2,RNA1,8:pair-50:pair$$$V2.0"
molecule = Molecule.from_helm(parent_helm, monomer_library=library)
fig, ax = molecule.visualize_cartoon(title="Parent Molecule", show_base=True)
from helmshaker import Molecule
parent_helm = "CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].[fl2r](C)[sp].[m](U)p.[fl2r](A)p.[m](A)p.[fl2r](U)p.[m](C)p.[fl2r](U)p.[m](C)p.[fl2r](C)p.[m](A)p.[fl2r](C)p.[m](U)p.[fl2r](U)p.[m](C)p.[fl2r](A)p.[m](U)p.[fl2r](C)p.[m](C)[sp].[m](U)[sp].[m](U)}|RNA2{[m](G)[sp].[m](G)[sp].[fl2r](A)p.[m](U)p.[fl2r](G)p.[m](A)p.[fl2r](A)p.[m](G)p.[fl2r](U)p.[m](G)p.[fl2r](G)p.[m](A)p.[fl2r](G)p.[m](A)p.[fl2r](U)p.[m](U)p.[fl2r](A)p.[m](G)[sp].[fl2r](U)[sp]}$RNA2,CHEM1,57:R2-1:R1|CHEM1,CHEM2,1:R2-1:R1|RNA2,RNA1,11:pair-47:pair|RNA2,RNA1,14:pair-44:pair|RNA2,RNA1,17:pair-41:pair|RNA2,RNA1,20:pair-38:pair|RNA2,RNA1,23:pair-35:pair|RNA2,RNA1,26:pair-32:pair|RNA2,RNA1,29:pair-29:pair|RNA2,RNA1,2:pair-56:pair|RNA2,RNA1,32:pair-26:pair|RNA2,RNA1,35:pair-23:pair|RNA2,RNA1,38:pair-20:pair|RNA2,RNA1,41:pair-17:pair|RNA2,RNA1,44:pair-14:pair|RNA2,RNA1,47:pair-11:pair|RNA2,RNA1,50:pair-8:pair|RNA2,RNA1,53:pair-5:pair|RNA2,RNA1,56:pair-2:pair|RNA2,RNA1,5:pair-53:pair|RNA2,RNA1,8:pair-50:pair$$$V2.0"
molecule = Molecule.from_helm(parent_helm, monomer_library=library)
fig, ax = molecule.visualize_cartoon(title="Parent Molecule", show_base=True)
UNA Walk: Generate All Variants¶
Loop through each sugar position on RNA1 and create a variant with UNA at that position.
In [3]:
Copied!
# Get the number of residues in RNA1
num_residues = len(molecule.data.polymers["RNA1"].residues)
print(f"Number of residues in RNA1: {num_residues}")
# Get the number of residues in RNA1
num_residues = len(molecule.data.polymers["RNA1"].residues)
print(f"Number of residues in RNA1: {num_residues}")
Number of residues in RNA1: 21
In [4]:
Copied!
# Generate UNA walk variants
variants = []
for position in range(1, num_residues + 1):
# Start fresh from the parent each time
variant = Molecule.from_helm(parent_helm, monomer_library=library)
# Replace the sugar at this position with UNA
variant.modify("replace_single", polymer_id="RNA1", pos=position, type="sugar", new_monomer="una")
# Store the result
variants.append({
"position": position,
"helm": variant.to_helm()
})
print(f"Generated {len(variants)} variants")
# Generate UNA walk variants
variants = []
for position in range(1, num_residues + 1):
# Start fresh from the parent each time
variant = Molecule.from_helm(parent_helm, monomer_library=library)
# Replace the sugar at this position with UNA
variant.modify("replace_single", polymer_id="RNA1", pos=position, type="sugar", new_monomer="una")
# Store the result
variants.append({
"position": position,
"helm": variant.to_helm()
})
print(f"Generated {len(variants)} variants")
Generated 21 variants
In [5]:
Copied!
# Show the first few variants
for v in variants[:3]:
print(f"Position {v['position']}: {v['helm'][:80]}...")
# Show the first few variants
for v in variants[:3]:
print(f"Position {v['position']}: {v['helm'][:80]}...")
Position 1: CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[una](A)[sp].[fl2r](C)[sp].m(U)p.[fl2r](A)p.m(A)...
Position 2: CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].[una](C)[sp].m(U)p.[fl2r](A)p.m(A)...
Position 3: CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].[fl2r](C)[sp].[una](U)p.[fl2r](A)p...
Visualize a Specific Variant¶
You can visualize any specific variant to see the modification.
In [6]:
Copied!
# Visualize variant at position 5
variant_5 = Molecule.from_helm(parent_helm, monomer_library=library)
variant_5.modify("replace_single", polymer_id="RNA1", pos=5, type="sugar", new_monomer="una")
fig, ax = variant_5.visualize_cartoon(title="UNA at Position 5", show_base=True)
# Visualize variant at position 5
variant_5 = Molecule.from_helm(parent_helm, monomer_library=library)
variant_5.modify("replace_single", polymer_id="RNA1", pos=5, type="sugar", new_monomer="una")
fig, ax = variant_5.visualize_cartoon(title="UNA at Position 5", show_base=True)
Export All Variants¶
You can export the variants as a list of HELM strings for further processing.
In [7]:
Copied!
# Create a simple table of variants
import pandas as pd
df = pd.DataFrame(variants)
df.head(10)
# Create a simple table of variants
import pandas as pd
df = pd.DataFrame(variants)
df.head(10)
Out[7]:
| position | helm | |
|---|---|---|
| 0 | 1 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[una](A)[sp].[... |
| 1 | 2 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 2 | 3 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 3 | 4 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 4 | 5 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 5 | 6 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 6 | 7 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 7 | 8 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 8 | 9 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |
| 9 | 10 | CHEM1{[Ahx]}|CHEM2{[OVal]}|RNA1{[fl2r](A)[sp].... |