Professional Documents
Culture Documents
Bio 5
Bio 5
deo
Poravnanja - 1. deo
Čest zadatak bioinformatičara je pronalaženje poravnanja dve ili više sekvenci. Poravnanje podrazumeva
pronalaženje najboljeg poklapanja karaktera sekvenci sa ciljem pronalaženja regiona velike sličnosti.
Uzmimo, na primer, aminokiselinsku FASTA sekvencu histonskog proteina H1 kod čoveka:
MTKKSTDHPKYSDMIVAAIQAEKNRAGSSRQSIQKYIKSHYKVGENADSQIKLSIKRLVT
TGVLKQTKGVGASGSFRLAKSDEPKKSVAFKKTKKEIKKVATPKKASKPKKAASKAPTKK
PKATPVKKAKKKLAATPKKAKKPKTVKAKPVKASKPKKAKPVKPKAKSSAKRAGKKK
MTENSTSAPAAKPKRAKASKKSTDHPKYSDMIVAAIQAEKNRAGSSRQSIQKYIKSHYKV
GENADSQIKLSIKRLVTTGVLKQTKGVGASGSFRLAKGDEPKRSVAFKKTKKEVKKVATP
KKAAKPKKAASKAPSKKPKATPVKKAKKKPAATPKKAKKPKVVKVKPVKASKPKKAKTVK
PKAKSSAKRASKKK
Sekvence ova dva proteina nisu potpuno identične, ali se primećuje velika sličnost. Poravnanje ove dve
sekvence, koje bi prepoznalo regione velike sličnosti moglo bi izgledati ovako:
H10_HUMAN 1 -----------------MTKKSTDHPKYSDMIVAAIQAEKNRAGSSRQSI 33
.:|||||||||||||||||||||||||||||||
H10_MOUSE 1 MTENSTSAPAAKPKRAKASKKSTDHPKYSDMIVAAIQAEKNRAGSSRQSI 50
H10_HUMAN 34 QKYIKSHYKVGENADSQIKLSIKRLVTTGVLKQTKGVGASGSFRLAKSDE 83
|||||||||||||||||||||||||||||||||||||||||||||||.||
||:||||||||||:|||||||||:||||||||||:||||||||||||||.
|||||||||||.||.||||||||||||.||||||||||||.|||
Pri čemu uspravne crte | označavaju uspešno uparivanje karaktera, horizontalne crtice - uparivanje sa
prazninom (insercije/delecije) dok tačkice :. označavaju manju ili veću grešku prilikom uparivanja različitih
karaktera.
Iz navedenih rezultata poravnanja može se zaključiti da je H1 protein ostao većinski nepromenjen tokom
evolucije ove dve vrsta organizama.
Kao što se može primetiti, na algoritmima poravnanja je zadatak poravnati slične, ne nužno identične,
sekvence. Iz ove nedovoljno precizne definicije zadatka mogu se na različite načine konstruisati algoritmi sa
različitim ciljevima poravnanja.
Većina ovih algoritama su predstavnici algoritama dinamičkog programiranja pa će iz tog razloga prvo biti
definisan jednostavniji problem, kako bi se bolje shvatila problematika i način rešavanja problema.
Rešenje
Svaki problem dinamičkog progamiranja, kao i njegovo rešenje, mogu se predstaviti direktnim acikličnim
grafom (DAG). Svaki početak ulice i raskrsnica mogu se predstaviti čvorom grafa sa oznakom i ⋅ m + j , gde
i i j predstavljaju raskrsnice i -te i j -te ulice. Vrednosti i = 0 i j = 0 označavaju početke odgovarajućih
ulica. Zanimljivosti (atrakcija koje se nalaze u ulici) predstavljene su granama grafa, između čvorova, sa
težinama koje odgovaraju zanimljivosti atrakcija u delu ulice između navedenih raskrsnica. Čvorovi grafa se
mogu predstaviti matricom dimenzija (n + 1) x (m + 1) dok se grane mogu predstaviti uređenim trojkama
(u, v, w), gde su u i v čvorovi između kojih se nalazi grana a w težina grane.
In [1]:
'''
| | |
1 2 1
| | |
| | |
1 2 1
| | |
'''
n = 3
m = 3
edges = [(0,1,1),(1,2,1),(0,3,1),(1,4,2),(2,5,1),(3,4,1),(4,5,1),(3,6,1),(4,7,2),(5,8,1
),(6,7,1),(7,8,2)]
Kako nije efikasno prolaziti kroz listu grana svaki put kada je potrebno proveriti težinu, listu možemo pretvoriti
u odgovarajuću mapu [u] → [v] → [w]
In [2]:
def edges_to_map(edges):
edge_map = {}
if u not in edge_map:
edge_map[u] = {}
edge_map[u][v] = w
return edge_map
In [3]:
edges_to_map(edges)
Out[3]:
1: {2: 1, 4: 2},
2: {5: 1},
3: {4: 1, 6: 1},
4: {5: 1, 7: 2},
5: {8: 1},
6: {7: 1},
7: {8: 2}}
Turista se može kretati samo u pravcima dole i desno. Za svaku poziciju u grafu može se izračunati cena
optimalnog puta (sa najviše posećenih atrakcija) rekurentnom formulom:
S core i−1,j + edge i−1,j
S corei,j = max {
S core i,j−1 + edge i,j−1
Drugim rečima, do polja i, j može se doći odozgo, sa polja i − 1, j - čime se na skor sakupljen do te
pozicije dodaje vrednost grane od i − 1, j do i, j, ili sa leve strane, sa polja i, j − 1 čime se na skor
sakupljen do te pozicije dodaje vrednost grane od i, j − 1 do i, j. Pri takvom izboru, za novi skor bira se
maksimum ponuđenih mogućnosti. Do polja koja se nalaze na gornjoj ivici (i = 0) može se doći samo
kretanjem sa leve strane pa je cena dolaska sa gornje strane uvek jednaka nuli. Analogno važi i za levu ivicu
(j + 0 ). Iz tog razloga, matricu S core možemo proširiti dodatnim redom i kolonom sa gornje i leve strane,
čije će vrednosti biti jednake nuli a obilazak će počinjati od pozicije (i = 1, j = 1). Mapiranje koordinata u
čvorove grafa će sada biti (i − 1) ∗ m + (j − 1). Vrednost na poziciji (n + 1, m + 1) će biti vrednost
maksimalnog skora atrakcija pri obilasku dok će odabir puta, pri svakom izboru na putu od početka do kraja
obilaska, odgovarati koracima optimalnom obilaska grada.
In [4]:
edge_map = edges_to_map(edges)
u_up = (i - 2) * m + (j - 1)
u_left = (i - 1) * m + (j - 2)
v = (i - 1) * m + (j - 1)
# u_up
# |
# u_left -- v
top_edge = 0
if u_up in edge_map:
if v in edge_map[u_up]:
top_edge = edge_map[u_up][v]
left_edge = 0
if u_left in edge_map:
if v in edge_map[u_left]:
left_edge = edge_map[u_left][v]
if S[i][j] == from_top:
backtrack[i][j] = 1
else:
backtrack[i][j] = -1
i = n
j = m
while i != 1 or j != 1:
if backtrack[i][j] == 1:
i -= 1
else:
j -= 1
In [5]:
print(f'Putanja: {path}')
Ukupan skor: 7
Putanja: [0, 1, 4, 7, 8]
Primer
Niska: ABCDEFG
Najduža zajednička podsekvenca između dve niske je najduža podsekvenca koja se nalazi i u jednoj u u
drugoj niski. Problem se rešava dinamičkim programiranjem. Pretpostavimo da je poznata dužina
podsekvence do prefiksa i − 1 u prvoj niski i j u drugoj niski i prefiksa i u prvoj i j − 1 u drugoj niski.
Najduža podsekvenca do prefiksa i i j produžuje duži od prethodna dva prefiksa za jedan, ako su karakteri
niska1i i niska2j jednaki ili ostaje skor dužeg prefiksa ukoliko se karakteri razlikuju. Vrednosti najdužih
podsekvenci svih prefiksa obe niske mogu se predstaviti tabelom, tako da se na poziciji i, j nalazi dužina
najduže podsekvence prefiksa i karaktera prve niske i j karaktera druge niske. Tabela će biti dimenzija
(n + 1) x (m + 1) , gde su n i m dužine niski, pri čemu nulti red i nulta kolona odgovaraju praznim
prefiksima (dužine 0) odgovarajućih niski. Za bazu rekurzije može se uzeti činjenica da poravnanje praznog
prefiksa sa bilo čime daje najdužu podsekvencu dužine 0. Iz toga sledi da su vrednosti u redu i = 0 i koloni
j = 0 jednake nuli. Vrednost u polju n, m popiunjene tabele odgovara odgovoru na polazni problem - dužini
In [6]:
n = len(v) + 1
m = len(w) + 1
backtrack[i][0] = (i - 1, 0)
backtrack[0][j] = (0, j - 1)
from_left = s[i][j - 1] + 0
if s[i][j] == from_top:
backtrack[i][j] = (i - 1, j)
backtrack[i][j] = (i, j - 1)
else:
backtrack[i][j] = (i - 1, j - 1)
i = n - 1
j = m - 1
lcs = ""
if backtrack[i][j] == (i - 1, j - 1):
(i, j) = backtrack[i][j]
In [7]:
v = 'AGTCGTGATCGTTGTA'
w = 'GTATGAA'
3. Edit rastojanje
Edit rastojanje između dve niske predstavlja minimalni broj edit operacija (dodavanje karaktera, brisanje
karaktera, zamena karaktera) potrebnih da se jedna niska transformiše u drugu.
Primer:
Niska 1: 'ABC'
Niska 2: 'ABD'
Niska 1: 'AB'
Niska 2: 'ABC'
Niska 1: 'ABC'
Niska 2: 'AD'
Edit rastojanje između navedenih je takođe 2 (jedno brisanje karaktera i jedna zamena)
Ovaj algoritam je takođe predstavnik algoritama dinamičkog programiranja i može se koristiti za potrebe
poravnanja bioloških sekvenci. Konstrukcija algoritma je ponovo rekurzivna, Neka je poznato edit rastojanje
prefiksa prve niske do karaktera na poziciji i − 1 i karaktera na poziciji j − 1 druge niske. Edit rastojanje
prefiksa i i j može se dobiti na tri načina:
karakteri na pozicijama niska1[i] i niska2[j] su jednaki → Edit rastojanje je jednako edit rastojanju
prefiksa i − 1 i j − 1 (cena je 0 jer nije potrebno primeniti ni jednu dodatnu edit operaciju)
ili se
karakteri na pozicijama niska1[i] i niska2[j] se razlikuju → Potrebno je izvršiti još jednu edit operaciju
kako bi se karakteri zamenili
niska1[i] se poravnava sa niska2[j-1] → izvršeno je brisanje karaktera iz niske 1 (još jedna edit
operacija)
niska1[i-1] se poravnava sa niska2[j] → izvršeno je brisanje karaktera iz niske 2 (još jedna edit
operacija)
Za poravnanje prazne sekvence sa sekvencom duzine x potrebno je x edit operacija. Upravo će to biti baza
rekurzije. Potrebno je konstruisati tabelu dinamičkog programiranja dimenzije (n + 1) x (m + 1) , gde su n
i m dužine niski. Dodatak +1 podrazumeva da nulti red i nulta kolona predstavljaju poravnanja sa praznom
niskom. U svakom koraku, cilj je odabrati ponuđeni put koji vodi ka minimalnom edit rastojanju.
Red i = 0 i kolona j = 0 odgovaraju poravnanjima sa praznom niskom, prema tome vrednost u tim poljima
će biti S0,j = j i Si,0 = i.
In [8]:
n = len(v) + 1
m = len(w) + 1
s[i][0] = i
backtrack[i][0] = (i - 1, 0)
s[0][j] = j
backtrack[0][j] = (0, j - 1)
from_left = s[i][j - 1] + 1
if s[i][j] == from_top:
backtrack[i][j] = (i - 1, j)
backtrack[i][j] = (i, j - 1)
else:
backtrack[i][j] = (i - 1, j - 1)
i = n - 1
j = m - 1
v_align = ''
w_align = ''
if backtrack[i][j] == (i - 1, j):
v_align = v[i - 1] + v_align
else:
(i, j) = backtrack[i][j]
In [9]:
v = 'ACGTGACCTGGA'
w = 'GTGT'
print(v_align)
print(w_align)
Edit distance: 8
ACGTGACCTGGA
--GTG---T---
4. Globalno poravnanje
Ponekad edit rastojanje nije dovoljno da bi objasnilo određena poravnanja već je potrebno dodeliti određeni
skor, odnosno kaznu, za odgovarajuća poravnanja karaktera. Osnova za tako nešto može se naći u
biološkim osobinama proteinskih sekvenci. Aminokiseline imaju odgovarajuća svojstva koja utiču na način na
koji se protein uvija u svoju 3D strukturu. Ako bi se između dve sekvence poravnale aminokiseline sa
potpuno drugačijim svojstvima, kazna takvog poravnanja bila bi veća nego u slučaju poravnanja dve
aminokiseline sa sličnim svojstvima. Takođe kazna pojave praznine u poravnanju ne mora biti jednaka kazni
loše uparenih aminokiselina. Iz tih razloga, pojavljuje se pojam globalnog poravnanja sa različitim kaznama
za loša poravnanja i praznine i pozitivnim skorovima za dobra poravnanja. Za razliku od edit rastojanja, koje
računa minimum rastojanja prethodnih prefiksa, globalno poravnanje (Needleman-Wunsch algoritam) računa
maksimum ukupnog skora. Ukupan skor poravnanja dve niske nalazi se u polju n + 1, m + 1 tabele.
S core0,j = j ∗ GapS core
In [10]:
GAP_PENALTY = -2
MISSMATCH_PENALTY = 0
MATCH_SCORE = 1
n = len(v) + 1
m = len(w) + 1
backtrack[i][0] = (i - 1, 0)
backtrack[0][j] = (0, j - 1)
else:
if s[i][j] == from_top:
backtrack[i][j] = (i - 1, j)
backtrack[i][j] = (i, j - 1)
else:
backtrack[i][j] = (i - 1, j - 1)
i = n - 1
j = m - 1
v_align = ''
w_align = ''
if backtrack[i][j] == (i - 1, j):
v_align = v[i - 1] + v_align
else:
(i, j) = backtrack[i][j]
In [11]:
v = 'ACGTGACCTGGA'
w = 'GTGT'
print(v_align)
print(w_align)
ACGTGACCTGGA
--GTG---T---
5. Lokalno poravnanje
Globalno poravnanje pokušava da poravna cele sekvence jednu sa drugom. U nekim situacijama potrebno je
samo pronaći lokalna poravnanja sa najvećim skorom (najbolje očuvane regione). Modifikacija globalnog
poravnanja u lokalno je jednostavna. Baza rekurzije podrazumeva da vrednosti reda i = 0 i j = 0 budu 0,
kako se ne bi kažnjavale početne praznine dok se za rezultat lokalnog poravnanja ne uzima vrednost iz
tabele na poziciji n + 1, m + 1 već maksimalna vrednost u tabeli.
S core0,j = 0
S corei,0 = 0
In [12]:
GAP_PENALTY = -1
MISSMATCH_PENALTY = -2
MATCH_SCORE = 1
n = len(v) + 1
m = len(w) + 1
backtrack[i][0] = (i - 1, 0)
backtrack[0][j] = (0, j - 1)
else:
if s[i][j] == from_top:
backtrack[i][j] = (i - 1, j)
backtrack[i][j] = (i, j - 1)
else:
backtrack[i][j] = (i - 1, j - 1)
max_value = float('-inf')
for i in range(n):
for j in range(m):
max_value = s[i][j]
max_i = i
max_j = j
i = max_i
j = max_j
v_align = ''
w_align = ''
while s[i][j] != 0:
if backtrack[i][j] == (i - 1, j):
v_align = v[i - 1] + v_align
else:
(i, j) = backtrack[i][j]
v_align_len = len(v_align)
w_align_len = len(w_align)
if m < n:
v_align = v
else:
w_align = w
In [13]:
v = 'ACGTGACCTGGA'
w = 'GTGGT'
print(v_align)
print(w_align)
ACGTGACCTGGA
--GTGGT------