[{"content":"개요 이전 1C 역상보 문제의 결과에서 우리는 Vibrio cholerae 의 가장 빈번한 상위 4 종류의 서열 중 ATGATCAAG와 CTTGATCAT가 서로 상보적 관계에 있음을 알 수 있었다. 이 결과는 DnaA box를 찾았다는 결론을 뒷받침하는 근거로 여겨질 수 있을까?\n이전 장에서 찾은 위의 결과는 유전체의 처음부터 끝까지 전체 영역에서 등장하는 Pattern이었다. 즉, 우리가 찾은 결과는 분포에 대한 정보를 포함하지 않는다. 따라서 결과 pattern들은 유전체 상에서 고르게 분포 되어있을 수 있다.\n이것이 왜 중요할까? 다음의 그림을 통해, 유전체의 복제가 어떻게 진행되는지 그 양상을 확인할 수 있다. Bacteria의 경우 Origin of Replication이 하나의 영역에 존재하는 것을 확인할 수 있고, Eukaryotes의 경우 Origin of Replication이 여러 영역에 걸쳐 밀집되어 있음 을 알 수 있다.\n따라서 우리는 이전 장에서 찾은 pattern들이 어느 위치에 존재하는지 아는 것이 중요하다. 위치에 대한 정보를 알게 되면 우리가 찾은 pattern들이 얼마나 밀집되어있는지를 확인할 수 있기 때문이다.\n그러므로 이번 장에서는 임의의 입력 pattern이 어느 위치에 존재하는지 그 위치를 반환하는 함수를 구현하였다.\nFig 1. (A) Bacterial Replication Initiation, (B) Eukaryotic Replication Initiation - Wikipedia\nProblem Input: 찾을 pattern, 유전체 Output: 유전체 상 pattern이 등장하는 위치 function: 찾으려는 문자열 pattern이 유전체 상에서 등장하는 모든 위치(인덱스)를 리스트로 반환 Pseudo-code 1 2 3 4 5 6 7 8 9 10 11 findPatternIndices(genome, pattern) patternSize = len(pattern) genomeSize = len(genome) indexList \u0026lt;-- empty List for i \u0026lt;- 0 to genomeSize if genome[i:i+patternSize] == pattern add i to indexList return indexList Evaluation Time Complexity 입력크기: $\\left\\vert genome \\right\\vert = n$, $\\left\\vert pattern \\right\\vert = k $\nline[2] ~ line[5]: 변수 초기화 → $O(1)$ line[7]: 모든 genome의 시작점을 순회 → $O(n)$ line[8]: 문자열 비교 연산 → $O(k)$​[^1] line[9]: (일반적으로) $O(1)$[^2] 어떠한 경우에도 모든 문자열을 순회해야 correct solution이 도출됨 Total Time Complexity: $O(1) + O(n) \\times O(k) \\approxeq O(nk)$\nImplementation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 주어진 text 문자열에서 pattern이 발생한 문자열의 인덱스를 리스트로 반환하는 함수 def findPatternIndices(text, pattern): # 발생 빈도를 저장할 배열 선언 OccList = [] # pattern을 셀 수 있는 모든 문자열을 시작점으로 순회 for idx in range(len(text) - len(pattern) + 1): # 시작 문자열부터 pattern 길이만큼의 부분문자열(substring)이 pattern을 형성하면 그 인덱스를 배열에 저장 if(text[idx:idx + len(pattern)] == pattern): OccList.append(idx) return OccList Rosalind Code in Github Discussion Points Summary 이전 결과는 유전체 상에서 pattern이 고르게 등장하는 경우를 포함할 수 있음 (밀집된 경우보다 Randomness가 더 큼) 따라서 빈번한 pattern이 얼마나 밀집되어있는지 (Localization)에 대한 정보가 필요함 이를 알기 위해서는 특정 Pattern이 등장하는 위치를 찾아야 함 Implementation Strategy 유전체 문자열을 순회하며 주어진 pattern만큼 문자열을 가져옴 가져온 유전체의 부분 문자열과 주어진 pattern을 비교 서로 같으면 그 위치를 저장 후 순회가 끝나면 모든 위치를 반환 Implications 이를 통해 찾은 이전 결과 ATGATCAAG의 등장 위치는 116556, 149355, 151913, 152013, 152394, 186189, 194276, \u0026hellip; 으로 총 17번임\n이 중 위의 151913, 152013, 152394은 매우 가까이 위치한 곳으로, 나머지 경우에서는 이와 같이 군집을 이루지 않음\n즉, 이 영역이 DnaA box의 영역일 수 있음을 시사함 [^3]\nReference Compeu, P., Pevzner, P. (2018). Bioinformatics Algorithms 3/e. 에이콘 출판사 Replication Initiation figure: https://en.wikipedia.org/wiki/Origin_of_replication [^1]: 두 문자열 중 가장 긴 것만큼 포인터가 순회하여 비교할 수 있음. strcmp(str1, str2) → O(s), where s = max(str1, str2) [^2]: 초기 할당된 메모리 크기를 벗어날 경우, 배열의 크기 재조정을 위해 값 복사가 일어날 수 있음 → O(l), where l =len(indexList) [^3]: 단순히 가까워서라기보다, 통계적 근거에 기반한 추론임\n","date":"2024-02-20T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/05-ch1-1d/","title":"1D 패턴 일치 문제"},{"content":"개요 이전 장의 결과로부터 우리가 찾은 frequent pattern ATGATCAAG는 그 위치가 군집을 이룬다는 발견 을 했다. 이 결과에 이어, 이번 장에서는 pattern이 정말 군집을 이루는지 정량적으로 분석하도록 하였다.\n군집을 이룬다는 것을 어떻게 정의할 수 있을까? 직관적으로, 특정 문자열 pattern이 전체 유전체에서 등장하는 \u0026ldquo;밀도\u0026quot;가 높을 때 군집을 형성한다고 생각할 수 있다. 일반적으로 밀도는 단위 부피 당 질량을 의미하며, 다음과 같이 정의된다.\n$$\\rho = w / V$$\n$(where,\\rho:,density,,w:,weight,,V:,volume )$\n이러한 접근에 근거하여, 군집을 이루는 기준을 다음과 같이 정의할 수 있다.\n전체 문자열 중 임의의 영역 L에서 pattern의 등장하는 빈도 수\n이번 장에서는 특히 등장하는 빈도수가 어느 정도 이상인 pattern을 찾기 위해, t라는 parameter를 추가하였다. 이는 곧 등장 빈도수가 t번 이상인 pattern만 확인한다는 것을 의미한다. 또, pattern의 길이를 k-mer로 고정하였다.\n이를 정리하자면, 다음의 그림과 같이 전체 문자열 Genome 내에서 L만큼의 영역의 substring을 가져오고, 이 substring 내에서 등장하는 모든 k-mer의 빈도를 센 뒤, 빈도가 t번보다 많이 나오면 이를 clump를 이루는 pattern으로 반환한다.\n$$\\text{Find all K such that f(K, S) ≥ t for any S = genome[i:i+L],}$$\n$$\\text{where i≥0 and i+L ≤ length of genome}$$\n$$ \\text{K for k-mer patterns,}; \\text{f for function to find clumps}$$\nIdea to calculate and find the clumps in genome\nProblem Input: 문자열 유전체 Text, 정수 k, L, t Output: Text의 L 길이의 임의의 영역에서 t번 이상의 빈도로 등장하는 k-mer Pseudo-code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 findClummps(genome, k, L, t) clumpPatterns \u0026lt;-- empty set for i \u0026lt;-- 0 to |genome|- L + 1 substring = genome[i:i+L] for j \u0026lt;-- 0 to |substring| - k + 1 pattern = substring[j:j+k] count the occurrence of the pattern in the substring if count \u0026gt;= t: add pattern to clumpPatterns return clumpPatterns Evaluation Time Complexity 위 알고리즘의 경우 genome의 모든 문자열을 시작점으로 하여 모든 경우의 수를 순회한다. 이는 correct solution을 보장하지만, 어느정도 중복되는 부분이 있을 수 있다. 이에 대한 간략한 논의는 Discussion에서 해보도록 한다.\n입력 크기: $\\left\\vert genome \\right\\vert = n, k, L, t$\n$\\text{Constraints: n ≥ L ≥ k,,t}$\nline[2]: 변수 초기화 → $O(1)$\nline[4] ~ line[5]: 유전체에서 매 iteration마다 유전체의 모든 문자열에 대해 L 길이만큼 substring으로 slicing 해옴 → $O(n - L + 1) \\approxeq O(n)$​\nline[7] ~ line[8]: substring의 모든 문자열에서 k-mer를 pattern으로 slicing 해옴 → $O(L-k+1)\\approxeq O(L)$\nline[9]: 이전에 구현한 PatternCount를 사용할 수 있음 → $O(Lk)$ [^1]\nline[11] ~ line[14]: 비교연산, set.add()[^2], 반환 → $O(1)$\nTotal Time Complexity: $O(1) + O(n) \\times O(L) \\times O(Lk) + O(1) \\approxeq O(L^2 \\cdot n \\cdot k)$\nImplementation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # 주어진 유전체 문자열에서 크기가 L인 윈도우만큼을 순회하여 각 윈도우에서 t만큼의 빈도로 등장하는 k-mer를 배열로 출력 def myFindClumps(genome, k, L, t): # Clumps에 해당하는 k-mer를 저장할 리스트 선언 ClumpPattern = set() # genome에서 길이가 L인 substr을 가져오는 반복문 for window in range(len(genome)-L+1): # 주어진 windoe 범위 내에 있는 substr(length: L)을 slicing substr = genome[window:window+L] # L-substr에서 모든 문자열에 대한 순회 시작 for i in range(L-k+1): # 각 시작점에서 형성되는 k-mer를 pattern으로 설정 pattern = substr[i:i+k] # 현재 window의 substr에서 pattern의 등장 횟수를 저장 count = PatternCount(substr, pattern) # 만약 기록된 pattern의 빈도가 t와 같다면 ClumpPattern 에 저장 if count \u0026gt;= t: ClumpPattern.add(pattern) return ClumpPattern Rosalind Code in Github Discussion Points Summary 단지 빈번한 단어를 찾는 것은 \u0026ldquo;어디에서 얼마나 등장하는지\u0026rdquo; 에 대한 위치 정보를 포함하지 못함 이는 clump라는 군집을 정의함으로써 등장하는 빈도를 마치 밀도처럼 계산할 수 있음 Implementation Strategy genome에서 L만큼 substring을 slicing substring에서 k만큼 pattern을 slicing pattern의 substring에서의 등장 빈도 확인 t 이상이면 clumpPattern에 저장 Implications 이번에 구현한 알고리즘은 정답을 보장하지만 효율적이지는 못함 substring을 가져올 때 현재 인덱스의 L과 다음 인덱스의 L에서 L-2개의 문자열이 중복되기 때문 (아래 그림 참조) 이에 대한 논의는 추후 충전소 - 빈도 배열을 통해 해결할 수 있다. 한편, PatternCount 대신 window loop에 FrequentWords를 사용함으로써 더 간단하게 구현할 수 있다.[^3] Redundancy in Brute-Force Algorithm to find clumps\nReference Compeu, P., Pevzner, P. (2018). Bioinformatics Algorithms 3/e. 에이콘 출판사 [^1]: 이전 PatternCount(text, pattern)에서 입력 text가 substring이 되므로, O(Lk)가 됨 [^2]: python에서 집합은 hash 구조로 이루어져 있으며, 평균적으로 O(1), 최악의 경우 O(n)이지만 내부 최적화로 O(1)로 간주 [^3]: 이 경우 역시도 시간복잡도는 $O(L^2 \\cdot n \\cdot k)$ 로 동일하다\n","date":"2024-02-20T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/06-ch1-1e/","title":"1E 군집 찾기 문제"},{"content":"개요 앞서 단백질의 특이성(specificity)에 대해 이야기하였다. 세포 내에서 일어나는 이러한 단백질의 특이성은 마치 단백질에 눈이 달린 것 처럼 작동한다고 생각하기 쉽지만, 단백질의 특이성 또한 결국 물리화학적 법칙을 따르는 분자의 운동에 불과하다.\n즉, 단백질 분자가 외부 계의 물리적 힘 - 특히 열 - 에 의해 진동하거나 부유하는 brownian motion을 통해 돌아다니다가, 적절한 DNA 서열과 유효충돌이 일어나야 상호작용이 일어나는 것이다.\n따라서, 우리는 다음과 같은 가설을 세울 수 있다.\n1. DnaA box가 많을수록 DnaA protein과의 유효충돌 횟수가 커진다.\n2. DnaA box가 많이 존재한다면, 이 서열들 중 일부에 변이가 일어나도 결합을 방해하는 데에 미치는 영향을 줄일 수 있다.\n그러므로, 위 가설에 따르면 문제는 반복되는 특정 서열을 찾는 것으로 귀결된다. 하지만 여전히 우리는 이 특정 서열 의 길이가 어느정도인지 알지 못하기 때문에, 임의의 길이를 가지는 특정 서열을 k-mer로 정의한다.\nk-mer: 길이가 k인 문자열\n이렇게 k-mer pattern을 임의로 정한 뒤, 주어진 입력 Text에서 얼마나 등장하는지 그 횟수를 count 할 수 있다. 가장 간단한 방법으로, 다음과 같이 모든 각 문자열을 시작점으로 하는 k-mer와 비교할 수 있다.\nFig 1. index가 0일 때 주어진 text에서 pattern(ACTA)찾기\nFig 2. index가 1일 때 주어진 text에서 pattern(ACTA)찾기\nProblem Input: 전체 문자열 Text, Text에서 찾으려는 문자열 Pattern Output: Text에서 등장하는 Pattern의 횟수 function: Pattern $\\to$ count Pseudo-code 1 2 3 4 5 6 7 PatternCount(Text, Pattern) count \u0026lt;- 0 for i \u0026lt;- 0 to |Text| - |Pattern| Pattern\u0026#39; = Text(i, |Pattern|) if Pattern\u0026#39; == Pattern count \u0026lt;-- count + 1 return count Evaluation Time Complexity 이 알고리즘의 경우 Brute-force[^1] 방식으로, 모든 경우의 수를 순차적[^2]으로 확인한다.\n입력 크기: $\\left\\vert Text \\right\\vert = n, \\left\\vert Pattern \\right\\vert = k$​\n$\\text{Constraints: n ≥ k}$\nline[1]: Pattern이 등장하는 횟수를 저장할 변수 선언[^3] $\\to O(1)$ line[2]: Text와 Pattern의 입력 크기를 각각 $\\left\\vert Text \\right\\vert$ , $\\left\\vert Pattern \\right\\vert$ 이라 할 때, Text 문자열에서 Pattern 길이의 부분 문자열(substring)을 형성할 수 있는 모든 시작점은 0번째부터 $\\left\\vert Text \\right\\vert - \\left\\vert Pattern \\right\\vert $ 번째까지 이다. → $O(n-k)$ line[3]: 현재 순번에서 가능한 부분 문자열 형성 - $Text(i, \\left\\vert Pattern \\right\\vert)$ 만큼을 slicing 해오는 연산 $\\to O(1)$ line[4]: sliced substring과 Pattern의 비교 연산 → $O(k)$ [^4] line[5]: count 변수의 산술 연산 → $O(1)$ line[3] ~ line[5]의 경우 상수항의 시간이 소요된다. Worst case: $n \u0026raquo; k \\to n-k \\approx n$ Total Time Complexity: $O(1) + O(n-k) \\times (O(1)+O(k)+O(1)) \\approxeq O(nk)$\nImplementation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 전체 Text에서 주어진 Pattern의 등장 횟수를 반환하는 함수 def PatternCount(text, pattern): # 결과로 반환할 변수 선언 count = 0 # pattern의 시작점이 될 수 있는 영역을 모두 순회 for idx in range(len(text) - len(pattern) + 1): # 현재 인덱스를 시작으로 하는 pattern이 입력 pattern과 같다면 count if(text[idx:idx + len(pattern)] == pattern): count += 1 return count Rosalind Code in Github Discussion Points Summary 주어진 유전체에서 어떤 특징을 찾음으로써 특이성에 대한 단서를 얻을지도 모름 이러한 특징에 대한 접근으로, 유전체에서 빈번하게 나타나는 패턴을 찾는 접근을 시도해 볼 수 있음 Implementation Strategy 이러한 빈번한 패턴을 찾는 문제를 해결하기 위해 다음과 같은 전략을 통해 코드를 구성하였다.\n주어진 전체 text에서 가능한 모든 substring의 조합을 확인함 가능한 모든 조합의 substring을 주어진 pattern과 일치하는지 확인하여 일치할 때마다 그 count를 셈. Implications 유전체 상에서 임의의 서열이 얼마나 등장 하는지 확인할 수 있게 되었다. 이러한 임의의 서열은 DnaA box와 같이 ori 를 구성하는 주요 서열의 후보군 으로 생각할 수 있다. 하지만, 이 방법만 가지고는 유전체 상에서 \u0026ldquo;어느 위치에 반복적인 서열이 있는지\u0026rdquo; 를 확인하기는 어렵다. 다음 문제에서는, 이번에 구현한 함수를 통해 가장 많이 등장하는 단어가 어떤 단어인지 확인하는 방법에 대해 알아보도록 한다.\nReference Compeu, P., Pevzner, P. (2018). Bioinformatics Algorithms 3/e. 에이콘 출판사 Craig, N., Cohen-Fix, O., Green, R., Greider, C., Storz, G., \u0026amp; Wolberger, C. (2010). Molecular biology: Principles of genome function. Oxford University Press. [^1]: 가능한 모든 경우의 수를 탐색하는 알고리즘 기법을 의미함 [^2]: 순차 탐색 - 순차적으로 탐색하는 기법 [^3]: 대입 연산 [^4]: 두 문자열의 길이는 k이므로, k번의 비교가 필요, 즉, $\\left\\vert Pattern \\right\\vert = k = m$ 이므로 $O(m)$\n","date":"2024-01-30T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/02-ch1-1a/","title":"1A 단어 세기"},{"content":"개요 앞선 문제에서는 입력된 pattern이 얼마나 등장하는지, 즉, Pattern → count 였다면, 이번에는 pattern에 대한 조건을 다루어 볼 수 있다. 즉, 원하는 길이의 Pattern 중 가장 많이 등장하는 k-mer를 찾을 수 있다.\n앞선 목표: Pattern → count\n이번 목표: k → Pattern\nBrute-Force를 통해 알고리즘을 수행하면, 이전에 구현한 PatternCount를 이용해, 다음과 같은 아이디어를 이용할 수 있다.\n문자열의 모든 문자 하나하나를 시작점으로 하는 k-mer pattern 에 대해 주어진 입력 문자 text를 PatternCount로 count Fig 1. index가 0일 때 pattern count\nFig 2. index가 1일 때 pattern count\nProblem Input: 전체 문자열 Text, Text에서 찾으려는 문자열 Pattern의 길이 k Output: 가장 빈번하게 등장하는 k-mer function: k $\\to$ Pattern Pseudo-code 1 2 3 4 5 6 7 8 FrequentWords(Text, k) FrequentPatterns \u0026lt;-- empty set count = [] for i \u0026lt;- 0 to |Text| - k Pattern = Text(i, i + k) count[i] = PatternCount(Text, Pattern) find index of max count return Text(index, index + k) Evaluation Time Complexity 입력 크기: $\\left\\vert Text \\right\\vert = n, \\left\\vert Pattern \\right\\vert=k$​\n$\\text{Constraints: n ≥ k}$\nline[2] ~ line[3: 대입연산 $\\to O(1)$​ line[4]: k-mer 형성이 가능한 모든 문자 순회 반복문 $\\to O(n-k)$ line[5]: 문자열 슬라이싱 $\\to O(1)$ line[6]: PatternCount $\\to O(nk)$ line[7]: find index of max count $\\to O(n\\log{n}) ~ O(n^2)$ 최대값을 찾는 과정은 정렬을 수행[^1] Total Time Complexity: $O(1) + O(n-k) \\times (O(1) + O(nk)) + O(n^2) \\approxeq O(n^2k)$\nImplementation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # 주어진 text에서 특정 words의 빈도수를 계산하여 빈도수가 가장 높은 k-mer를 기록하고 반환하는 함수 def FrequentWords(text, k): # 입력된 text와 같은 크기로 등장 빈도수를 동일한 인덱스에 저장하는 리스트 선언 count = [0 for i in range(len(text))] # 출력할 최대 빈도수의 pattern을 저장할 set 선언 - uniqueness로 중복 값을 저장하지 않음 FrequentPattern = set() # i-th value가 가지는 k-mer의 빈도 수를 count라는 배열에 저장 for i in range(len(text) - k + 1): pattern = text[i:i+k] count[i] = PatternCount(text, pattern) # count의 원소가 maxCount인 pattern을 FrequentPattern으로 저장 maxCount = max(count) for i in range(len(text) - k): if count[i] == maxCount: FrequentPattern.add(text[i:i+k]) return FrequentPattern Rosalind Code in Github Discussion Points Summary 주어진 임의의 서열에서 가장 빈번한 pattern이 무엇인지 확인할 수 있다. Implementation Strategy 이전에 구현한 PatternCount를 통해 가능한 모든 k-mer 조합이 전체 text에서 등장하는 횟수를 모두 배열(count)에 기록 기록된 배열 중 가장 큰 count를 가진 pattern을 다시 반환함 가장 많이 등장하는 pattern이 어떤 pattern인지 확인할 수 있었다. implications 이전 장과 서론의 문제 해결 아이디어를 더불어 생각해보면, k → Pattern → count로 어떤 k-mer가 가장 많이 등장하는지도 확인할 수 있을 것이다. 이러한 접근은 아무 정보도 주어지지 않은 상태에서 어떤 임의의 반복 서열이 가장 많은 count를 가지는지 확인할 수 있는 단서로 활용할 수 있을지도 모른다. 다음 문제에서는, 이번에 찾은 다양한 Pattern들과 연관된 상보적인 서열 의 등장 횟수를 고려하여, 빈번한 pattern이 우연이 아닌 상관관계/타당성을 가진다고 말할 수 있는지 알아보도록 하겠다.\nReference Compeu, P., Pevzner, P. (2018). Bioinformatics Algorithms 3/e. 에이콘 출판사 Craig, N., Cohen-Fix, O., Green, R., Greider, C., Storz, G., \u0026amp; Wolberger, C. (2010). Molecular biology: Principles of genome function. Oxford University Press. [^1]: 최대값/최소값을 찾는 알고리즘은 정렬을 수행해야 한다. 기수정렬을 제외한 일반적인 merge/quick sort의 경우 $O(n^2)$, 힙정렬의 경우 $O(nlog{n})$의 시간복잡도가 소요된다.\n","date":"2024-01-30T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/03-ch1-1b/","title":"1B 빈번한 단어 문제"},{"content":"개요 핵산의 방향성 핵산(nucleic acid) 는 인산-당-염기 라는 하나의 단위가 뉴클레오타이드(nucleotide) 를 이루며, 이러한 뉴클레오타이드가 여러 개 모여서 형성된 것이다.\nFig 1. The Structure of the Nucleotide\n즉, nucleotide는 당 분자에 의해 인산과 염기가 연결된다. 이러한 당의 종류로 Ribose 또는 Deoxyribose가 있는데, 이들은 각각 RNA 또는 DNA의 nucleotide를 구성한다. 이 두 종류의 당은 5개의 탄소 원자로 구성되어 5탄당이라 하는데, 다음과 같이 각각의 탄소에 1번부터 5번까지의 번호를 매긴다. 두 당은 그 구조와 화학식이 서로 유사하지만, Ribose에는 2\u0026rsquo;의 위치에 hydroxyl group(2\u0026rsquo;-OH)이, Deoxyribose에는 2\u0026rsquo;의 위치에 hydrogen(2\u0026rsquo;-H)이 존재한다.\nFig 2. The Types of Sugars in the RNA \u0026amp; DNA - Deoxyribose(left) and Ribose(right)\n따라서 핵산의 방향성은 염기와 인산을 결합하는 당의 방향성에 의해 결정된다. 하나의 단일 뉴클레오타이드에서, 인산은 당의 5\u0026rsquo;에 위치한 탄소와 결합되며, 서로 다른 두 개의 뉴클레오타이드는 첫 번째 뉴클레오타이드 당의 3\u0026rsquo; 위치에서 다음 뉴클레오타이드 당의 5\u0026rsquo;에 결합된 인산과 공유결합[^1]한다. 그러므로, 생물학에서 핵산의 방향성을 이야기 할 때, \u0026ldquo;5\u0026rsquo; → 3\u0026rsquo;의 방향성을 가진다\u0026rdquo; 고 말한다.\nFig 3. A single strand of DNA\n핵산의 상보성 한편, 핵산에서 염기는 일반적으로[^2] 4종류의 염기를 가진다. DNA에서는 A, T, G, C를, RNA에서는 T 대신 U를 가진다. 이러한 4종류의 염기는 DNA나 RNA가 이중 가닥을 이룰 때 서로 상보적으로 수소 결합한다. 즉, 4종류의 염기 중 2가지가 서로 짝을 이루어 수소결합을 하며, 이를 상보적 이라 한다.\n수소결합은 분자를 이루는 원자 중 전기음성도가 강하고 크기가 작은 F, O, N이 인접한 수소와 분자 간 쌍극자 힘에 의해 인력을 가지는 현상을 말한다. 염기에서는 아래의 그림과 같이 질소와 산소가 수소 결합을 할 만큼 충분히 존재하며, 여러 위치 에서 수소 결합이 일어날 수 있다.\n그 중 일반적인 경우에 형성되는 주요한 수소 결합을 Watson-Crick base-paring(Canonical base-paring)[^3]이라 하며, A와 T(U)가 2개의 수소 결합을, G와 C가 3개의 수소 결합을 이룬다.\nFig 4. Watson-Crick base-paring\n역상보 서열 앞선 핵산의 방향성과 상보성을 고려하여, 역상보 서열이 무엇인지 알아보도록 하자.\nDNA가 이중 나선을 이룰 때, 상보성에 의해 각 가닥의 방향성이 정해진다. 즉, 어떤 한 가닥의 DNA가 이루는 방향이 5\u0026rsquo; → 3\u0026rsquo;으로 진행된다고 할 때, 또 다른 한 가닥의 DNA 역시 5\u0026rsquo; → 3\u0026rsquo; 의 방향성을 가진다. 이 두 가닥이 상보성에 의해 수소결합을 이루게 되면 다음의 그림과 같이 각 가닥이 서로 반대 방향으로 진행된다.\n그러므로, 우리는 어떤 임의의 서열을 읽을 때, 기준 방향을 5\u0026rsquo; → 3\u0026rsquo;으로 정하고 그 서열을 읽는다.\nFig 5. A complementary sequence that progresses in the reverse direction\n한 가지 예시를 들어보자. 임의의 서열 \u0026ldquo;ACTGAT\u0026quot;가 있다고 할 때, 이 서열의 상보적인 서열은 \u0026ldquo;TGACTA\u0026rdquo; 가 된다. 그러나, 방향성을 고려하여 표시하면 다음과 같다.\n5\u0026rsquo; - ACTGAT - 3'\n3\u0026rsquo; - TGACTA - 5'\n우리는 이 두 서열을 읽을 때, 주어진 5\u0026rsquo;→3\u0026rsquo;의 가닥을 주형 가닥(template strand) 이라 하고, 그 반대편의 가닥을 상보적인 가닥(complementary strand) 라 한다. 위 두 예시 서열을 기준 5\u0026rsquo; → 3\u0026rsquo;의 방향으로 읽으면, 다음과 같이 읽을 수 있다.\n5\u0026rsquo; - ACTGAT - 3'\n5\u0026rsquo; - ATCAGT - 3'\n이때, 주어진 주형 가닥에 대해 5\u0026rsquo; → 3\u0026rsquo; 의 방향으로 읽은 상보적인 서열을 \u0026ldquo;역상보 서열\u0026rdquo; 이라 한다.\n따라서 이번 문제에서는 주어진 서열에 대한 역상보 서열을 찾아보도록 한다.\nProblem Input: DNA 서열 Output: 역상보 서열 function: 주어진 DNA 서열에 대한 역상보 서열 찾기 Pseudo-code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ReverseComplement(seq) complement = \u0026#39;\u0026#39; * |seq| for i \u0026lt;- 0 to |seq|: if seq[i] == \u0026#39;A\u0026#39;: complement[i] = \u0026#39;T\u0026#39; else if seq[i] == \u0026#39;T\u0026#39;: complement[i] = \u0026#39;A\u0026#39; else if seq[i] == \u0026#39;G\u0026#39;: complement[i] = \u0026#39;C\u0026#39; else if seq[i] == \u0026#39;C\u0026#39;: complement[i] = \u0026#39;G\u0026#39; else raise error reverseComplement = \u0026#39;\u0026#39; * |seq| for i \u0026lt;- 0 to |seq|: reverseComplement[i] = complement[|seq| - 1 - i] return reverseComplement Evaluation Time Complexity 입력 크기: $\\left\\vert seq \\right\\vert = n$\nline[2]: 상보적인 서열을 저장할 변수 complement 초기화 → $O(1)$​ line[4]: 주어진 입력 서열 seq의 모든 문자열(염기)를 순회 → $O(n)$ line[5] ~ line[9]: 해당 순번의 염기 서열에 상응하는 상보적 서열을 complement에 저장 → $O(1)$ line[11]: 역상보 서열을 저장할 빈 문자열 변수 reverseComplement 초기화 → $O(1)$ line[13] ~ line[14]: 상보 서열의 모든 문자열을 순회하면서 뒤집어서 저장 → $O(n)$ 모든 경우에 대해, 최악의 경우와 최선의 경우가 존재하지 않으며, 모든 문자열을 순회해야 correct solution이 도출됨. 따라서, 다음 알고리즘은 항상 일정한 시간복잡도를 가지는 알고리즘임 Total Time Complexity: $O(1) + O(n) \\times O(1) + O(1) + O(n) \\approxeq O(n)$\nImplementation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 주어진 서열을 상보적인 서열로 바꿔 반환하는 함수 def Complement(seq, reverse=False): # 상보적인 서열이 어떤 것인지 저장하는 dictioinary 선언 basepair = {\u0026#39;A\u0026#39; : \u0026#39;T\u0026#39;, \u0026#39;T\u0026#39;: \u0026#39;A\u0026#39;, \u0026#39;G\u0026#39; : \u0026#39;C\u0026#39;, \u0026#39;C\u0026#39; : \u0026#39;G\u0026#39;} # 반환할 상보적 서열 문자열 변수 선언 CompSeq = \u0026#39;\u0026#39; # 입력 서열의 각 문자 하나씩에 대한 상보적인 문자를 추가함. # A, T, G, C 가 아닌 다른 염기에 대한 상보적인 서열은 \u0026#39;?\u0026#39;로 추가 for base in seq: CompSeq += basepair.get(base, \u0026#39;?\u0026#39;) # reverse 매개변수를 참으로 받을 때 역상보 서열을 반환 (5\u0026#39; -\u0026gt; 3\u0026#39;) if reverse: return CompSeq[::-1] # 그렇지 않으면 상보 서열만 반환 (3\u0026#39; -\u0026gt; 5\u0026#39;) else: return CompSeq Rosalind Code in Github Discussion Points Summary 임의의 DNA/RNA는 일반적인 경우 서로 상보적 인 서열을 가지고 있다. 각 DNA/RNA는 방향성을 가지고 있으며, 생물학에서 이를 읽는 기준 방향을 5\u0026rsquo; → 3\u0026rsquo; 으로 한다. 주어진 주형 가닥에 대한 상보적인 서열을 기준 방향으로 읽었을 때 역상보 서열 이라 한다. Implementation Strategy 주어진 서열의 상보적인 서열을 구한다. 상보적인 서열을 반대 방향으로 읽는다. Implications 책의 예시에서, Vibrio cholerae 의 가장 빈번한 상위 4개 9-mer가 \u0026ldquo;ATGATCAAG\u0026rdquo;, \u0026ldquo;CTTGATCAT\u0026rdquo; 임 이 두 서열은 서로 역상보 서열의 관계에 있으며, DnaA box를 구성할 것이라는 가설을 이끌 수 있음 실제로 DnaA box에 결합하는 DnaA 단백질은 DNA의 주형 가닥에 결합할 지, 상보적 가닥에 결합할 지 구분하지 않음 즉, 역상보 서열이 유전체에 존재하는 것은, 그 서열과 그 서열의 역상보 서열이 DnaA box를 구성할 것이라는 가설을 지지함 Reference Compeu, P., Pevzner, P. (2018). Bioinformatics Algorithms 3/e. 에이콘 출판사 Craig, N., Cohen-Fix, O., Green, R., Greider, C., Storz, G., \u0026amp; Wolberger, C. (2010). Molecular biology: Principles of genome function. Oxford University Press. [^1]: 이러한 공유결합을 phospho-diester bond라 한다. [^2]: 일반적이지 않은 경우, chemical modificatioin이 일어난 기타 종류의 염기를 가진다. pseudo-uridine, m6A 등의 예시가 있다. [^3]: 앞선 각주 1에서 언급된 바와 같이, chemical modification이 일어난 염기나, 기타 DNA/RNA의 구조적 특징에 의해 새로운 수소 결합의 종류가 존재한다.(Non-canonical base-pairing)\n","date":"2024-01-30T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/04-ch1-1c/","title":"1C 역상보 문제"},{"content":"목차 Chapter 1. DNA 복제는 유전체의 어디서부터 시작되는가? 서론 Introduction 본문 1A 단어 세기 1B 빈번한 단어 문제 1C 역상보 문제 1D 패턴 일치 문제 1E 군집 찾기 문제 최소 비대칭 문제 해밍 거리 문제 대략적인 패턴 일치 문제 미스매치가 있는 빈번한 단어 문제 미스매치와 역상보의 빈번한 단어 문제 에필로그 도전문제: Salmonella enterica의 DnaA box 고세균에서 복제 기점 찾기 충전소 빈도 배열 패턴과 숫자를 서로 변환하기 정렬을 사용해 빈번한 단어 찾기 군집 찾기 문제 해결 미스매치를 포함한 자주 나오는 문제 해결 문자열 이웃 생성 정렬로 미스매치를 포함한 빈번한 단어 찾기 ","date":"2024-01-29T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/00-toc/","title":"Bioinformatics Algorithm - Table of Contents"},{"content":" \u0026ldquo;DNA 복제는 유전체의 어디서부터 시작되는가?\u0026rdquo;\n개요 이 장에서는 유전체의 복제 과정이 시작되는 ori (Origin of Replication)을 간단한 예시를 통해 찾는 방법과 알고리즘을 학습한다.\n이러한 단순한 경우로서 ori 를 찾기 위한 다음의 몇가지 단서들을 이용해 유전체에서 해당 조건을 만족하는 motif 서열(책에서는 pattern, k-mer)을 탐색하였다.\n유전체에서 얼마나 자주 등장하는가? 자주 등장한다면, 그 pattern의 reverse complement 등장 횟수도 높은 빈도로 등장하는가? 유전체의 일정 영역에서 빈도수가 높은가?(즉, 밀도있게 등장하는가?; localization) G-C ratio(%)의 비율이 비대칭적인가? 유사한 문자열의 등장 빈도는 어떠한가? 서론 세포에서는 다양한 생물학적 과정을 수행하기 위해, 특정 서열이 반복적으로 나타난다.\n생물학에서 단백질과 특이성(Specificity) 우리 몸에서 중요한 구성요소이며 다양한 역할을 수행하는 분자로, 단백질이 있다. 단백질은 정확한 표적에 정확히 작용하기 위해 특이성(specificity) 이라는 특징을 가지는데, DNA의 예를 들면 특정 단백질에 대해 이와 특이적으로 결합하는 특정 DNA 서열을 가진다.\nDNA의 이중 나선 구조와 복제 한편, DNA의 복제(replication)는 생물의 가장 기본적인 구성 단위인 세포가 그 수를 늘리기 위해 필요한 과정인데, 이중 나선으로 이루어진 DNA를 복제하기 위해서는 이를 풀어내는 과정이 필수불가결하다.\n특히, 이번 장에서는 원형의 유전체를 가지는 bacterial genome에 대한 복제를 기준으로 전체적인 흐름이 진행되는데, 원형의 유전체는 이중 나선의 끝과 끝이 닫힌 형태(closed form)이기 때문에 복제가 시작되는 영역이 어디에 위치해 있는지를 아는 것은 중요하며, 이 영역을 origin of replication(ori)이라 한다.\nDnaA box 앞서 설명한 바와 같이, 유전체는 복제가 시작되기 위해 이중 나선을 풀어주는 과정이 필요하다. 따라서, ori라는 영역에는 이중 나선을 풀어주는 단백질이 결합하는 DNA 영역이 존재한다. 이 과정에 관여하는 단백질과 DNA 서열 쌍을 DnaA protein - DnaA box(sequence)라 한다.\nFig 1. DnaA box binding mechanism\n이러한 DnaA box - DnaA protein의 mechanism을 다음과 같이 간략히 정리할 수 있다.\nori 에는 DnaA box와 AT-rich DNA element가 구성되어있음. DnaA box에 DnaA protein이 결합함 DnaA box - DnaA protein의 결합이 AT-rich region을 unwinding (AT 결합은 2중 결합, 상대적으로 약함) DnaC가 DnaB helicase에 결합함 DnaB-DnaC complex가 DNA에 결합되고 DnaC는 떨어져나감 → 이중 가닥의 구조를 열어 polymerase의 결합을 유도함\n마무리 정리하자면, 유전체 복제가 시작되는 영역을 찾기 위해서 DnaA box를 찾는 것은 중요하다.\n하지만, 유전체에서 이러한 DnaA box를 생물학자들이 직접 실험을 통해 찾는 것은 경제적/시간적으로 큰 비용이 부담되기 때문에 몇가지 조건들을 통해 컴퓨터 과학자들이 이를 찾아내는 것이 효율적이다.\n그러므로, 우리는 이번 장에서 DnaA box라는 암호를 찾는 방법에 대해서 논의해본다.\n","date":"2024-01-29T00:00:00Z","permalink":"https://blog.mulatta.io/post/bioinformatics-algorithm/01-ch1-intro/","title":"Chapter 1. Introduction"}]