A DNA string is a reverse palindrome if it is equal to its reverse complement. For instance, GCATGC is a reverse palindrome because its reverse complement is GCATGC. See Figure 2.
Given: A DNA string of length at most 1 kbp in FASTA format.
Return: The position and length of every reverse palindrome in the string having length between 4 and 12. You may return these pairs in any order.
DNA seq 로부터 palindrom을 찾는 문제입니다. 단순한 Palindrom을 생각한다면 이런 걸 생각했을 지도 모르겠습니다. 예를 들어 "토마토". 앞으로 읽어도 토마토이고 거꾸로 읽어도 토마토이니 palindrom이라고 할 수 있죠. 하지만 DNA에서의 팰린드롬은 조금 다릅니다. 위에서 예시를 들어줬듯이 GCATGC의 경우 오른쪽에서 왼쪽으로 읽는 것과 왼쪽에서 오른쪽으로 읽는 것과는 차이가 있습니다.
하지만 중요한 점은 DNA helix에서 3`과 5` 은 서로 반대로 읽어야 함으로
위와 같이 변하여 원래 예제인 GCATGC 와 동일하게 변하게 됩니다.
이를 DNA 펠린드롬이라고 합니다.
\n
로 잘라주어야 합니다.input_processor = lambda i: i.strip().split("\n")
get_dna_seq = lambda lines: ''.join([line for line in lines if not line.startswith(">")])
def get_all_cases(dna_seq):
print(f"{get_all_cases.__name__} is running")
global cases, idxes, length, memory # 글로벌 변수를 선언해야 재귀 함수에서 리스트나 셋이 리셋되지 않습니다.
memory = set() # 다이나믹 프로그래밍
cases = list() # 경우의 수들
idxes = list() # DNA 시퀀스에서 시작 인덱스
length = list() # 경우의 수의 DNA 서열 길이
# 위에서 말했던 재귀함수가 여기서 돌아갑니다.
def seq_tree(s, left_idx, right_idx):
# 오른쪽과 왼쪽 인덱스가 만나면 종료
if right_idx - left_idx == 0:
return
# 다이나믹 프로그래밍. memory에 있으면 종료
if s[left_idx:right_idx] in memory:
return
else:
memory.add(s[left_idx:right_idx]) # 메모리에 저장
cases.append(s[left_idx:right_idx]) # 경우의 수 저장
idxes.append(left_idx) # 시작 인덱스 저장
length.append(len(s[left_idx:right_idx])) # 경우의 수 길이 저장
seq_tree(s, left_idx + 1, right_idx) # 왼쪽으로
seq_tree(s, left_idx, right_idx - 1) # 오른쪽으로
# 실행하면 글로벌 변수로 선언했던 배열들이 모든 경우의 수와 관련 메타데이터를 기억하고 있습니다.
seq_tree(dna_seq, 0, len(dna_seq))
# 원본 DNA 시퀀스 데이터와 경우의수 및 메타데이터를 반환
return dna_seq, zip(idxes, cases, length)
def filter_seq(data_pacakge):
dna_seq, cases_zip = data_pacakge
cases_zip = map(lambda x:(x[0], x[1]), filter(lambda x: 4<=x[2]<=12 and x[2] % 2 == 0, cases_zip))
return dna_seq, cases_zip
def get_pelindrom(data_package):
print(f"{get_pelindrom.__name__} is running")
dna_seq, cases_zip = data_package # 필터링 된 데이터
palindroms = []
for idx, dna_frag in cases_zip:
matches = re.finditer(get_reverse_dna(dna_frag), dna_seq) # 실제 데이터에서는 같은 경우의 수가 원본 시퀀스에서 여러군데 발견될 수 있습니다.
match_idxes = [match.start() for match in matches] # 시작인덱스를 구합니다.
for match_idx in match_idxes:
if is_palindrom(dna_frag): # 펠린드롬인지 확인합니다.
palindroms.append((match_idx + 1, len(dna_frag))) # 펠린드롬이 맞다면 배열에 추가합니다.
return palindroms
def result_print(palindroms):
for start_idx, length in palindroms:
print(start_idx, length)
# compose는 toolz의 compose 함수를 사용하였습니다.
run = tz.compose(
result_print,
tz.curry(sorted, key=lambda x: x[0]),
get_pelindrom,
filter_seq,
get_all_cases,
get_dna_seq,
input_processor
)
run(test_dna_seq)
import regex as re
import toolz as tz
test_dna_seq = """
>Rosalind_24
TCAATGCATGCGGGTCTATATGCAT
"""
compl_dna = {
"A": "T",
"G": "C",
"C": "G",
"T": "A"
}
input_processor = lambda i: i.strip().split("\n")
get_dna_seq = lambda lines: ''.join([line for line in lines if not line.startswith(">")])
get_reverse_dna = lambda dna_seq: "".join(reversed([compl_dna[n] for n in dna_seq]))
def filter_seq(data_pacakge):
dna_seq, cases_zip = data_pacakge
cases_zip = map(lambda x:(x[0], x[1]), filter(lambda x: 4<=x[2]<=12 and x[2] % 2 == 0, cases_zip))
return dna_seq, cases_zip
def get_all_cases(dna_seq):
global cases, idxes, length, memory
memory = set()
cases = list()
idxes = list()
length = list()
def seq_tree(s, left_idx, right_idx):
if right_idx - left_idx == 0:
return
if s[left_idx:right_idx] in memory:
return
else:
memory.add(s[left_idx:right_idx])
cases.append(s[left_idx:right_idx])
idxes.append(left_idx)
length.append(len(s[left_idx:right_idx]))
seq_tree(s, left_idx + 1, right_idx)
seq_tree(s, left_idx, right_idx - 1)
seq_tree(dna_seq, 0, len(dna_seq))
return dna_seq, zip(idxes, list(cases), length)
def is_palindrom(dna_seq):
rev_seq = get_reverse_dna(dna_seq)
if rev_seq == dna_seq:
return True
return False
def get_pelindrom(data_package):
dna_seq, cases_zip = data_package
palindroms = []
for idx, dna_frag in cases_zip:
matches = re.finditer(get_reverse_dna(dna_frag), dna_seq, overlapped=False)
match_idxes = [match.start() for match in matches]
for match_idx in match_idxes:
if is_palindrom(dna_frag):
palindroms.append((match_idx + 1, len(dna_frag)))
return palindroms
def result_print(palindroms):
for start_idx, length in palindroms:
print(start_idx, length)
run = tz.compose(result_print, tz.curry(sorted, key=lambda x: x[0]), get_pelindrom, filter_seq, get_all_cases, get_dna_seq, input_processor)
run(test_dna_seq)