You are on page 1of 13

Primer analyse

October 6, 2023

1 Primer analyse (Kap. 20)


Jeg har taget den beslutning at skrive denne aflevering nummer to gennem Jubyter Notebook i VS-
code. Dette valgt skyldes, at jeg gerne vil have et mere søgbart resultat til senere brug. Imildertid
kræver dette valg, at jeg først og fremmest opsætter Conda-miljøet bioprog som vi benytter i dette
kursus. Dette metoden hvorpå jeg denne opsætning forekommer er beskrevet i Translating.
Imildertid før jeg overhovedet går igang med dette projekt, starter jeg med at placerer terminalen
i den korrekte mappe, via cd-kommandoen:
[ ]: cd Desktop/Bioinformatik/Projekter/Primer_analyse

Hvorefter jeg aktiverer bioprog-miljøet via kommandoen:


[ ]: conda activate bioprog

Da jeg både har skiftet kernel i VS-code til bioprog og placeret mig i mappen Genome_assembly,
hvor alle mine filer til dette projekt er gemt, manlger jeg kun at sikre at de korrekte filer er indlæst.
Derfor importerer jeg disse via import-kommandoen:
[2]: import foldingproject
import test_foldingproject

Så, nu er jeg endelig klar til at gå igang med selve projektet.

1.1 Baggrundsviden
Lad os sige, at du har en kort sekvens, som du vil bruge som PCR-primer. For at
vurdere, om det er en passende primer, skal du kende sekvensens smeltetemperatur.
Du skal også sørge for, at den ikke folder sig sammen, så den ikke kan binde sig til
den DNA-sekvens, du vil amplificere.
På kursussiden kan du downloade de filer, du skal bruge til dette projekt:
• foldingproject.py er en tom fil, hvor du skal skrive din kode.
• test_foldingproject.py er testprogrammet, der lader dig teste den kode, du skriver
i translationproject.py.
Læg filerne i en mappe dedikeret til dette projekt. På de fleste computere kan du
højreklikke på linket og vælge “Gem fil som…” eller “Download linket fil”.

1
1.2 Tæl antallet af baser i din kandidatprimer
Før du kan beregne smeltetemperaturen, skal du bestemme, hvor mange gange hver
base forekommer i sekvensen. Du kan antage, at de eneste tegn i strengen er A, T, G
og C.
Skriv en funktion, count_bases, som tager et argument:
1. En streng, som er en DNA-sekvens.
Funktionen skal returnere:
• En ordbog, hvori nøgler er strenge, der repræsenterer baser, og værdier er heltal,
der repræsenterer antallet af forekomster af hver base. Hvis en base ikke findes i
sekvensen, skal dens antal være nul.
Eksempel på brug: Hvis funktionen kaldes sådan:
[ ]: count_bases("ATGG")

Så skulle det vende tilbage (ikke nødvendigvis med nøgle-værdi-par i samme række-
følge):

[ ]: {"A":1, "C":0, "G":2, "T":1}

I min proces af at definere en funktion der returnerer en ordbog, hvori nøgler er strenge, der
repræsenterer baser og værdierne er heltal, der repræsenterer antallet af forekommer af hver base,
starter jeg med at opskrive en “opskrift” for hvorledes jeg tænker opgaven skal gribes an:
1. Definer en funktion ved navnet count_bases.
2. Undersøg hver indgang/base i en liste/DNA-sekvens.
3. Hvis den pågældende base ikke er i listen, så skal denne indsættes denne i en ordbog.
4. Hvis basen allerede er i ordbogen, skal dette antal adderes med 1.
På baggrund af ovenstående starter jeg med af definere en funktion med funktionsnavnet
count_bases, som tager et argument, nemlig en liste:
[ ]: def count_bases(dna):

Efterfølgende initialiserer jeg en ordbog med funktionsvariablen counts med nøgleværdipar, der
repræsenterer de fire baser A, T, G og C, med hver værdi sat til 0. Dette er den ordbog, der vil
blive brugt til at opbevare tællingerne:
[ ]: def count_bases(dna):
counts = {'A':0, 'T':0, 'G':0, 'C':0}

Da jeg ønsker at undersøge hver indgang i en liste, skal jeg benytte en for-løkke. Derfor opsætter
jeg en for-løkke der går gennem hver karakter i i den indtastede DNA-sekvens:
[ ]: def count_bases(dna):
counts = {'A':0, 'T':0, 'G':0, 'C':0}

2
for b in dna:

Efterfølgende skal jeg kontrollere for hver base (karakter) i DNA-sekvensen. Det vil sige, at for hver
base i DNA-sekvensen skal jeg kontrollere, hvilken base det er ved hjælp af if - og elif -betingelserne.
Så hvis basen er ‘A’, øges tællingen for ‘A’ i counts-ordbogen med 1. Derudover hvis basen er ‘G’,
øges tællingen for ‘G’ i counts-ordbogen med 1 og hvis basen er ‘T’, øges tællingen for ‘T’ i counts-
ordbogen med 1. Til sidst hvis basen ikke er ‘A’, ‘G’ eller ‘T’, antages det, at det er ‘C’, og tællingen
for ‘C’ i counts-ordbogen øges med 1:
[ ]: def count_bases(dna):
counts={}
for i in dna:
if i == 'A':
counts['A'] += 1
elif i == 'G':
counts['G'] += 1
elif i == 'T':
counts['T'] += 1
else:
counts['C'] +=1

Efter at have gennemgået hele DNA-sekvensen ønsker jeg til sidst at returnere counts-ordbogen,
der indeholder tællingerne for hver af de fire baser:
[ ]: def count_bases(dna):
counts = {'A':0, 'T':0, 'G':0, 'C':0}
for i in dna:
if i == 'A':
counts['A'] += 1
elif i == 'G':
counts['G'] += 1
elif i == 'T':
counts['T'] += 1
else:
counts['C'] +=1
return counts

Så nu har jeg opkskrevet en funktion count_bases der returnerer en ordbog, hvori nøgler er strenge,
der repræsenterer baser og værdierne er heltal, der repræsenterer antallet af forekommer af hver
base, så lad os teste om denne virker:
[10]: def count_bases(dna):
counts = {'A':0, 'T':0, 'G':0, 'C':0}
for i in dna:
if i == 'A':
counts['A'] += 1
elif i == 'G':
counts['G'] += 1

3
elif i == 'T':
counts['T'] += 1
else:
counts['C'] +=1
return counts

count_bases("ATGG")

[10]: {'A': 1, 'T': 1, 'G': 2, 'C': 0}

Dette viser, at der er 1 adenin (A), 1 thymin (T) og 2 guanin (G) i den givne DNA-sekvens,
hvilket passer med det forventede resultat. Da funktionen virker tilfredsstillende, indsætter jeg
denne funktionsdefinition af count_bases til vores foldingproject.py, hvorfor jeg benytter mig af
kommandoen %%writefile -a, som indsætter funktionen i bunden af scriptet:
[13]: %%writefile -a foldingproject.py

def count_bases(dna):
counts = {'A':0, 'T':0, 'G':0, 'C':0}
for i in dna:
if i == 'A':
counts['A'] += 1
elif i == 'G':
counts['G'] += 1
elif i == 'T':
counts['T'] += 1
else:
counts['C'] +=1
return counts

Appending to foldingproject.py

1.3 Beregn smeltetemperaturen


Når du kender basesammensætningen i din sekvens, kan du nu beregne smeltetem-
peraturen for det dobbeltstrengede DNA, der dannes, når din primer parrer sig med
sekvensen for at amplificere. Hvis primeren har mindre end 14 baser, er formlen for
beregning af smeltetemperatur:

𝑇 𝑒𝑚𝑝 = (𝐴 + 𝑇 ) ∗ 2 + (𝐺 + 𝐶) ∗ 4

Og hvis primeren har 14 baser eller mere, beregnes den sådan:

(𝐺 + 𝐶 − 16, 4)
𝑇 𝑒𝑚𝑝 = 64, 9 + 41 ∗
𝐴+𝑇 +𝐺+𝐶

A, T, C og G i formlerne repræsenterer tallene for A, T, C og G i DNA’et primer.

4
Du skal skrive en funktion, der anvender disse to formler korrekt ved at tage højde
for primerens længde.
Skriv en funktion, melting_temp, som tager et argument:
1. En streng, som er en DNA-sekvens (din primer).
Funktionen skal returnere:
• Et tal, som repræsenterer smeltetemperaturen af det dobbeltstrengede DNA
svarende til DNA-strengen givet som argument.
Eksempel på brug: Hvis funktionen kaldes sådan:
[ ]: melting_temp("ATG")

Skal returnere til:


[ ]: 8

For at skrive en funktion der tager højde for længden af DNA-strengen og beregner smeltetemper-
aturen af det dobbeltstrengede DNA, starter jeg igen med at opskrive en “opskrift” for hvorledes
jeg ønsker at gribe opgaven an:
1. Definer en funktion med funktionsnavnet melting_temp, som tager et arugment, nemlig en
DNA-streng.
2. Undersøge om længden af DNA-strengen er større eller mindre en 14 baser.
3. Hvis længden af DNA-strengen er mindre end 14 baser, så skal smeltetemperaturen bestemmes
via den første formel.
4. Hvis længden af DNA-strengen er større end 14 baser, så skal smeltetemperaturen bestemmes
via den anden formel.
Nå, men nu har jeg en opskrift på plads. På baggrund af denne starter jeg med at definerer en
funktion kaldet melting_temp som indlæser vores lyste og returnerer en smeltemperatur:
[ ]: def melting_temp(dna):

Efterfølgende ønsker jeg, at benytte den tidligere defineret funktion count_bases, som tæller antallet
af hver af de fire baser (A, T, C og G) i den givne DNA-sekvens og returnerer en ordbog med
tællingerne:
[ ]: def melting_temp(dna):
counts = count_bases(dna)

Nu da jeg har fået vores funktion til at beregne antallet af hver base i vores DNA-streng, ønsker
jeg at kontrollerer længden af den indtastede DNA-sekvens. For at gøre dette benytte jeg mig af
metoden len:
[ ]: def melting_temp(dna):
counts = count_bases(dna)
if len(dna) < 14:

5
Hvis længden af DNA-sekvensen er mindre end 14 baser, så sætter jeg funktionen til at beregne
smeltepunktet via formlen:

𝑇 𝑒𝑚𝑝 = (𝐴 + 𝑇 ) ∗ 2 + (𝐺 + 𝐶) ∗ 4

[ ]: def melting_temp(dna):
counts = count_bases(dna)
if len(dna) < 14:
temp = (counts['A'] + counts['T']) *2 + (counts['G'] + counts['C']) * 4

Hvis længden af DNA-sekvensen er 14 eller mere baser, sætter jeg funktionen til at benytte følgende
formel til at smeltepunktet:

(𝐺 + 𝐶 − 16, 4)
𝑇 𝑒𝑚𝑝 = 64, 9 + 41 ∗
𝐴+𝑇 +𝐺+𝐶

Denne formel er en empirisk model kendt som “nearest-neighbor” og tager hensyn til antallet af A,
T, G og C i sekvensen:
[ ]: def melting_temp(dna):
counts = count_bases(dna)
if len(dna) < 14:
temp = (counts['A'] + counts['T']) *2 + (counts['G'] + counts['C']) * 4
else:
temp = 64.9 + 41 * (counts['G'] + counts['C'] -16.4) / (counts['A'] +␣
↪counts['T'] + counts['G'] + counts['C'])

Til returnerer funktionen smeltepunktet, som den har beregnet:


[ ]: def melting_temp(dna):
counts = count_bases(dna)
if len(dna) < 14:
temp = (counts['A'] + counts['T']) *2 + (counts['G'] + counts['C']) * 4
else:
temp = 64.9 + 41 * (counts['G'] + counts['C'] -16.4) / (counts['A'] +␣
↪counts['T'] + counts['G'] + counts['C'])

return temp

Nå, men igen har jeg opskrevet en funktion. Denne funktion melting_temp tager højde for længden
af DNA-strengen og beregner smeltetemperaturen af det dobbeltstrenget DNA, så lad os teste om
funktionen virker:
[12]: def melting_temp(dna):
counts = count_bases(dna)
if len(dna) < 14:
temp = (counts['A'] + counts['T']) * 2 + (counts['G'] + counts['C']) * 4
else:

6
temp = 64.9 + 41 * (counts['G'] + counts['C'] - 16.4) / (counts['A'] +␣
↪ counts['T'] + counts['G'] + counts['C'])
return temp

print(melting_temp("ATG"))

8
Så da funktionen forudsiger at slemtepunktet er 8 grader Celcius for DNA-strengen ‘ATG’, virker
funktionen som den skal. Derfor indsætter jeg denne funktion i vores foldingproject.py:
[14]: %%writefile -a foldingproject.py

def melting_temp(dna):
counts = count_bases(dna)
if len(dna) < 14:
temp = (counts['A'] + counts['T']) * 2 + (counts['G'] + counts['C']) * 4
else:
temp = 64.9 + 41 * (counts['G'] + counts['C'] - 16.4) / (counts['A'] +␣
↪counts['T'] + counts['G'] + counts['C'])

return temp

Appending to foldingproject.py

1.4 Omvendt komplementer sekvensen


Det er muligt, at en del af primeren danner basepar med en anden del af primeren for
at danne en hårnålestruktur. For at finde ud af, om dette kan ske med din primer,
skal du være i stand til at finde det omvendte komplement af DNA-sekvensen. Det
omvendte komplement af en DNA-sekvens er et, hvor sekvensen af baser først vendes
om, og derefter erstattes hver base med sin Watson-Crick komplementære base.
Skriv en funktion, omvendt_komplement, som tager ét argument:
1. En streng, som er en DNA-sekvens.
Funktionen skal returnere:
• En streng, som repræsenterer det omvendte komplement af DNA-strengen givet
som argument.
Eksempel på brug: Hvis funktionen kaldes sådan:
[ ]: reverse_complement("AATTC")

Skal returnere til:


[ ]: "GAATT"

For at opskrive en funktion der tager et argument, nemlig en DNA-streng i form af en liste og
returnerer den komplementære DNA-streng som en streng, starter jeg med at opskrive en “opskrift”
for hvorledes jeg ønsker at gribe udfordringen an:

7
1. Definer en funktion med funktionsnavnet reverse_complement der taget et argument i form
af en liste.
2. Undersøg hver indgang fra den modsatte ende af DNA-strengen.
3. Finde den komplementære base.
4. Indsætte den komplementære base i en liste.
Nu da jeg har opskrevet en “opskrift” for hvorledes jeg ønsker at gribe opgaven an, starter jeg med
at definere en funktion med funktionsnavnet reverse_complement:
[ ]: def reverse_complement(dna):

Efterfølgende initialiserer jeg en en tom liste rc, som vil blive brugt til at opbygge den omvendte
komplementære sekvens:
[ ]: def reverse_complement(dna):
rc = []

Da jeg ønsker at undersøge hver indgang i vores DNA-streng i den omvendte rækkefølge, benytter
jeg en for-løkke til at iterere gennem DNA-sekvensen. Dette betyder, at den starter fra den sidste
base og går baglæns til den første base:
[ ]: def reverse_complement(dna):
rc = []
for i in range(len(dna)-1, -1, -1):

For hver base i vores DNA-streng ønsker jeg via vores for-løkke at kontrollere, hvilken base det er
ved hjælp af if- og elif-betingelserne. Det vil sige, at hvis basen er ‘A’, tilføjer den ‘T’ til rc-listen.
Hvis basen derimod er ‘G’, tilføjer den ‘C’ til rc-listen, og hvis basen er ‘T’, tilføjer den ‘A’ til
_rc-listen. Til sidst hvis basen ikke er ‘A’, ‘G’ eller ‘T’, antages det, at det er ‘C’, og den tilføjer
‘G’ til rc-listen:
[ ]: def reverse_complement(dna):
rc = []
for i in range(len(dna)-1, -1, -1):
if dna[i] == 'A':
rc.append('T')
elif dna[i] == 'G':
rc.append('C')
elif dna[i] == 'T':
rc.append('A')
else:
rc.append('G')

Efter at have gennemgået hele DNA-sekvensen i omvendt rækkefølge, ønsker jeg at benytte metoden
join til at konvertere listen rc til en streng og returnerer den omvendte komplementære DNA-
sekvens:

8
[ ]: def reverse_complement(dna):
rc = []
for i in range(len(dna)-1, -1, -1):
if dna[i] == 'A':
rc.append('T')
elif dna[i] == 'G':
rc.append('C')
elif dna[i] == 'T':
rc.append('A')
else:
rc.append('G')
rc = ''.join(rc)
return rc

Så nu har jeg defineret en funktion med navnet reverse_complement, som tager et argument, nemlig
en DNA-streng i form af en liste og returnerer den komplementære DNA-streng som en streng. Ved
jeg, at hvis vi fx kalder reverse_complement(“AATTC”), vil funktionen returnere ‘GAATT’, hvilket
er den omvendte komplementære sekvens til den indtastede DNA-sekvens:
[15]: def reverse_complement(dna):
rc = []
for i in range(len(dna)-1, -1, -1):
if dna[i] == 'A':
rc.append('T')
elif dna[i] == 'G':
rc.append('C')
elif dna[i] == 'T':
rc.append('A')
else:
rc.append('G')
rc = ''.join(rc)
return rc

reverse_complement("AATTC")

[15]: 'GAATT'

Så da funktionen virker som forventet indsættes denne i vores foldingproject.py:


[16]: %%writefile -a foldingproject.py

def reverse_complement(dna):
rc = []
for i in range(len(dna)-1, -1, -1):
if dna[i] == 'A':
rc.append('T')
elif dna[i] == 'G':
rc.append('C')

9
elif dna[i] == 'T':
rc.append('A')
else:
rc.append('G')
rc = ''.join(rc)
return rc

Appending to foldingproject.py

1.5 Tjek for hårnåle


Du vil gerne være i stand til at afgøre, om din primer kan foldes for at danne en
hårnål med et bestemt minimumsantal af på hinanden følgende basepar. Vi antager,
at hårnåleløkker altid er mindst fire baser lange, og at basepar i hårnålen kun kan
være Watson-Crick basepar. Her er et eksempel på en hårnål med fem basepar og en
løkke med fire baser (fire C’er):

[ ]: C C
C C
A-T
T-A
A-T
T-A
A-T

For at teste, om en sekvens kan danne en hårnål med mindst fire på hinanden føl-
gende basepar, skal du teste, om sekvensen indeholder en delsekvens af længde fire,
hvis omvendte komplement er identisk med en anden ikke-overlappende delsekvens.
For at tage højde for, at hårnålesløjfen er mindst fire baser lang, skal sådanne to
undersekvenser adskilles af mindst fire baser.
Skriv en funktion, has_hairpin, som tager to argumenter:
1. En streng, som er en DNA-sekvens.
2. Et heltal, som repræsenterer det mindste antal på hinanden følgende basepar i
hårnålene at søge efter.
Funktionen skal returnere:
• Sandt, hvis sekvensen indeholder en hårnål af mindst den angivne længde og Falsk
ellers.
Eksempel på brug: Hvis funktionen kaldes sådan:
[ ]: print(has_hairpin("ATATACCCCTATAT", 4))

Skal returnere til:


[ ]: True

Det er svært, så jeg vil give dig lidt hjælp. Her er funktionen med nogle dele mangler.

10
[ ]: def has_hairpin(s, k):
looplen = 4
for i in range(len(s)-k+1):
subs = # Hint A
right = # Hint B
revcl = reverse_complement(subs)
if revcl in right[looplen:]:
return True
return False

Tip A: Her skal du udtrække en delstreng med længden k, der starter ved i.
Tip B: Her skal du udtrække hele sekvensen til højre for substr.
Jubii, nu skal jeg ikke skrive en hel funktion igen. Så lad mig lige undersøge hvad den opskrevet
funktion has_hairpin enlig gør. Jamen først og fremmest ser jeg at funktionen tager to arugmenter
som input. Disse argumenter er s, som er en streng, der repræsenterer en DNA-sekvens, og k, som
er et heltal, som angiver længden af den sekvens, der skal kontrolleres for hairpin-strukturer. Så
det vil sige, at funktionen has_hairpin, har til formål, at afgøre, om den givne DNA-sekvens s
indeholder en hairpin-struktur af en bestemt længde k.
Så nu da vi har formålet på plads, ser vi at den første kode i funktionen er en variabel looplen som
er defineret til at have værdien 4. Dette repræsenterer længden af den sløjfe i hairpin-strukturen,
som den leder efter.
Efterfølgende ser vi en for-løkke som itererer gennem DNA-sekvensen s med et skridt af 1 base ad
gangen, indtil den når til len(s) - k + 1. Dette sikrer, at den undersøger alle mulige undersekvenser
af længde k i den oprindelige sekvens.
For hver undersekvens subs af længde k, udtrækker funktionen den resterende del af sekvensen til
højre for denne undersekvens og opbevarer den i variablen right.
Efterfølgende ser vi at funktionen gør brug for vores tidligere defineret funktion re-
verse_complement, som giver den komplementære DNA-streng. Derefter kontrollerer funktionen,
om den genererede reverse complement revcl findes i den resterende del af sekvensen right, med
undtagelse af de første looplen baser. Dette tjekker, om der er en hairpin-struktur, hvor subs kan
baseparre med revcl for at danne sløjfen. Hvis en hairpin-struktur findes, returnerer funktionen
True. Hvis ingen hairpin-struktur findes efter at have gennemgået alle mulige undersekvenser,
returnerer funktionen False.
Så på baggrund af ovenstående ved jeg, at subs = s[i:i+k] og right = s[(i+k):(len(s))], da disse er
slicing-operationer i Python, der bruges til at udtrække en del af en streng s og gemme denne del
i funktionsvariablerne subs og right:
[ ]: def has_hairpin(s, k):
looplen = 4
for i in range(len(s)-k+1):
subs = s[i:i+k]
right = s[(i+k):(len(s))]
revcl = reverse_complement(subs)
if revcl in right[looplen:]:

11
return True
return False

Så lad os teste om funktionen has_hairpin returnerer den ønskede værdi True for følgende DNA-
streng:
[17]: def has_hairpin(s, k):
looplen = 4
for i in range(len(s)-k+1):
subs = s[i:i+k]
right = s[(i+k):(len(s))]
revcl = reverse_complement(subs)
if revcl in right[looplen:]:
return True
return False

print(has_hairpin("ATATACCCCTATAT", 4))

True
Som det kan ses returnerer funktionen den ønskede værdi True, hvorfor jeg konkluderer, at funk-
tionen has_hairpin virker som den skal. Sagt på en anden måde ser vi, at indeholder en hairpin-
struktur af en bestemt længde k.
Så da funktionen værdier tilfredsstillende indsætter jeg denne funktion assemble_genome til vores
foldingproject.py, hvorfor jeg benytter mig af kommandoen %%writefile -a, som indsætter funktio-
nen i bunden af scriptet:
[18]: %%writefile -a foldingproject.py

def has_hairpin(s, k):


looplen = 4
for i in range(len(s)-k+1):
subs = s[i:i+k]
right = s[(i+k):(len(s))]
revcl = reverse_complement(subs)
if revcl in right[looplen:]:
return True
return False

Appending to foldingproject.py
Her til aller sidst vil jeg dog teste om funktionerne skrevet i foldingproject.py er tilstrækkelige
holdbare. Derfor kører jeg filen test_foldingproject.py via !python-kommandoen:
[19]: !python test_foldingproject.py

Ran 13 tests in 0.000s

OK

12
Som det kan ses i ovenstående output, er alle mine funktioner holdbare, hvorfor der printes OK.

13

You might also like