분자정보 저장 시스템
제3강 (3주차-2) · 인코딩 실습 — 내 이름을 DNA로
Python (Google Colab) | MATLAB Online | 8 bit → ATGC | GC% · 호모폴리머 | FASTA
송영준 Youngjun Song, Ph.D. · 인천대학교
← → 이동S 목차R 읽기 모드F 전체화면

오늘의 학습 성과
학습 목표 6가지
- Google Colab과 MATLAB Online에 접속해 코드를 실행·저장
- 변수·문자열·반복문·형 변환(ord/chr, double/char)을 두 언어로 사용
- 같은 작업을 두 언어로 비교하며 차이(0/1-index, 들여쓰기/end) 설명
- 자기 이름 → 8 bit → ATGC(2 bits/nt) 인코딩 코드 완성
- GC 함량·최대 호모폴리머 길이를 계산하고 제약 위반 판정
- DNA → 이름으로 디코딩해 왕복(round-trip) 검증 · FASTA 출력
인코딩 코드는 20줄이면 충분 · 어려운 것은 코드가 아니라 "규칙을 정확히 정의하는 것" — 코드는 규칙의 기록
오늘의 지도
75분 · 5개 파트 · 손을 계속 움직이는 시간
실습 소개 · 결과 미리보기
5분
Python · MATLAB 친숙해지기
30분
8 bit 이론 · 인코딩 기초
5분
이름 인코딩 (Python → MATLAB)
20분
확장 · 퀴즈 · 과제
10분
PART 2는 "설정 5분 → 실습 5분"을 Python·MATLAB 두 번 반복한 뒤 비교 · PART 4도 Python 먼저, MATLAB으로 같은 것을 다시
① 노트북 전원·Wi-Fi ② Google 계정 로그인 ③ MathWorks 계정(학교 이메일) 로그인 ④ 지난주 과제(손 인코딩 결과) 옆에 두기
실습 소개
What We Will Build Today
- 입력: 내 이름 · 출력: FASTA 한 줄
- 중간: 8 bit → ATGC → GC% · 호모폴리머 검사
- 되돌리기: DNA → 이름 (round-trip)
오늘 만들 것 — 결과 미리보기
"SONG" 네 글자가 16 nt DNA가 되기까지
01001110 01000111
16 nt
최대 연속 2
CCATCATT…
>SONG_2bit len=16 GC=43.8% maxrun=2 CCATCATTCATGCACT # 되돌리기(decode) → 'SONG' ✓ round-trip OK
- 글자 1개 = 8 bit = 4 nt → 영문 이름 4글자 = 16 nt
- 한글 이름은 UTF-8 3 byte/자 → "송영준" = 9 byte = 36 nt
- 검사 항목: GC 40~60% · 호모폴리머 ≤ 3 → 위반하면 "규칙을 바꿔야" 함 (PART 5)
- 왕복 검증: 인코딩 → 디코딩 결과가 원본과 같아야 인코더가 옳음
- 두 언어로 두 번 — 결과가 완전히 같아야 함 (같은 규칙이므로)
Python = 생물정보학·AI의 공용어(Biopython·PyTorch) · MATLAB = 공학·신호처리 실험실의 표준 · 둘 다 브라우저에서 무료로 시작 가능 · 하나를 알면 다른 하나는 "번역"
Python · MATLAB 친숙해지기
Two Languages, One Browser
- 언어 소개 5분 → Colab 설정 5분 → Python 실습 5분
- MATLAB Online 설정 5분 → MATLAB 실습 5분
- 두 번 반복한 뒤 나란히 비교 5분
언어 소개 — Python과 MATLAB
무엇이 같고 무엇이 다른가
| 항목 | Python | MATLAB |
|---|---|---|
| 성격 | 범용 언어 · 오픈소스 · 무료 | 수치계산 전용 · MathWorks 상용 · 대학 라이선스 |
| 주 사용처 | 생물정보학(Biopython) · AI(PyTorch) · 웹 · 자동화 | 신호처리 · 제어 · 영상 · 실험장비 연동 · 논문 그림 |
| 오늘의 환경 | Google Colab — 브라우저 · 설치 없음 · Google 계정 | MATLAB Online — 브라우저 · 설치 없음 · MathWorks 계정 |
| 문법 느낌 | 들여쓰기로 블록 · 0부터 세는 인덱스 · 리스트/딕셔너리 | end로 블록 · 1부터 세는 인덱스 · 행렬이 기본 |
| DNA 저장 분야 | DNA Fountain · 대부분의 공개 인코더/디코더 | 연구실 str2DNA · 신호 기반 시퀀서 데이터 처리 |
문법을 외우지 않음 · "이 줄이 무엇을 하는지"만 읽을 수 있으면 됨 · 막히면 옆 사람 화면과 비교 · 오류 메시지는 힌트이지 벌이 아님
Google Colab 환경 설정 — 5분
Google 계정 로그인
이름: week3_dna.ipynb
▶ 또는 Shift+Enter
결과 표시
"Colab Notebooks" 폴더
print("Hello, DNA")
x = 65
print(chr(x), ord("A"), format(x, "08b"))
for c in "DNA":
print(c, ord(c), format(ord(c), "08b"))Hello, DNA A 65 01000001 D 68 01000100 N 78 01001110 A 65 01000001
들여쓰기(for 다음 줄은 공백 4칸) · 따옴표 짝 · 대소문자(Print ✗) · 실행 안 됨 = 런타임 연결 대기(왼쪽 위 ▶ 옆 상태 확인)
Python 실습 1 — 변수 · 문자열 · 반복 · 변환 (5분)
오늘 쓸 도구 6개만
| 도구 | 뜻 | 예 |
|---|---|---|
| ord(c) | 글자 → 숫자(코드) | ord('S') → 83 |
| chr(n) | 숫자 → 글자 | chr(83) → 'S' |
| format(n,'08b') | 숫자 → 8자리 2진 문자열 | '01010011' |
| int(s, 2) | 2진 문자열 → 숫자 | int('01010011',2) → 83 |
| s[i:j] | 문자열 자르기 (0부터, j 미포함) | 'ABCDEF'[2:4] → 'CD' |
| ''.join(리스트) | 조각들을 이어 붙이기 | ''.join(['A','C']) → 'AC' |
name = "SONG"
for c in name:
print(c, ord(c), format(ord(c), "08b"))
bits = ""
for c in name:
bits = bits + format(ord(c), "08b")
print(bits, len(bits))
# 같은 일을 한 줄로 (내포 표기)
bits = "".join(format(ord(c), "08b") for c in name)출력: 01010011010011110100111001000111 32
자기 이름으로 바꿔서 실행 → 비트 수 = 글자 수 × 8 인지 확인
bits[0:8]은 무엇인가? bits[8:16]은? → int(bits[0:8], 2)가 83이면 성공 · 소문자 이름은 왜 다른 비트가 되는가(bit 5)
MATLAB Online 환경 설정 — 5분
MathWorks 계정(학교 메일)
또는 무료 Basic
week3_dna.m
또는 줄 선택 후 F9
· 작업공간에 변수
disp('Hello, DNA')
x = 65;
disp([char(x) ' ' num2str(double('A')) ' ' dec2bin(x, 8)])
for c = 'DNA'
fprintf('%s %d %s\n', c, double(c), dec2bin(double(c), 8));
endHello, DNA A 65 01000001 D 68 01000100 N 78 01001110 A 65 01000001
세미콜론(;)을 빼면 중간값이 전부 출력됨(오류 아님) · 문자열은 작은따옴표 · for … end 짝 · 무료 Basic = 월 20시간·5 GB·10개 제품·15분 유휴 시 세션 종료
MATLAB 실습 1 — 변수 · 문자열 · 반복 · 변환 (5분)
Python의 도구 6개에 대응하는 6개
| 도구 | 뜻 | 예 |
|---|---|---|
| double(c) | 글자 → 숫자(코드) | double('S') → 83 |
| char(n) | 숫자 → 글자 | char(83) → 'S' |
| dec2bin(n, 8) | 숫자 → 8자리 2진 문자열 | '01010011' |
| bin2dec(s) | 2진 문자열 → 숫자 | bin2dec('01010011') → 83 |
| s(i:j) | 문자열 자르기 (1부터, j 포함) | 'ABCDEF'(3:4) → 'CD' |
| [a b] · reshape | 이어 붙이기 · 모양 바꾸기 | ['A' 'C'] → 'AC' |
name = 'SONG';
for c = name
fprintf('%s %d %s\n', c, double(c), dec2bin(double(c), 8));
end
bits = '';
for c = name
bits = [bits dec2bin(double(c), 8)];
end
disp(bits); disp(length(bits))
% 같은 일을 한 줄로 (행렬 → 전치 → 한 줄)
bits = reshape(dec2bin(double(name), 8)', 1, []);출력: 01010011010011110100111001000111 32
dec2bin(double(name), 8)는 4×8 행렬 → '(전치) → reshape로 1×32
bits(1:8)은? (Python은 bits[0:8]) · 전치 없이 reshape하면 왜 비트 순서가 섞이는가 (열 우선 저장)
두 번 반복한 뒤 — 나란히 비교 (5분)
같은 일, 다른 표기
| 할 일 | Python | MATLAB |
|---|---|---|
| 주석 | # 설명 | % 설명 |
| 글자 ↔ 숫자 | ord('S') / chr(83) | double('S') / char(83) |
| 숫자 → 8 bit 문자열 | format(83, '08b') | dec2bin(83, 8) |
| 8 bit 문자열 → 숫자 | int('01010011', 2) | bin2dec('01010011') |
| 첫 8글자 | bits[0:8] (0부터, 끝 미포함) | bits(1:8) (1부터, 끝 포함) |
| 반복 | for c in name: (들여쓰기 블록) | for c = name … end |
| 이어 붙이기 | a + b / ''.join(list) | [a b] / strcat(a, b) |
| 출력 | print(x) / f"{x:.1f}" | disp(x) / fprintf('%.1f\n', x) |
| 매핑표 | m = {'00':'A', '01':'C'} | map = 'ACGT'; map(idx+1) |
차이는 "0/1 인덱스 · 블록 표기 · 행렬 기본" 세 가지뿐 · 나머지는 이름만 다른 같은 도구 → 한 언어를 알면 다른 언어는 번역
8 bit 이론 · 인코딩 기초
One Byte, Four Bases
- 1 글자 = 1 byte = 8 bit = 4 nt
- 매핑표 하나가 인코더의 전부
- 되돌리려면 역매핑표
8 bit → 4 nt — 인코딩 기초
규칙을 표로 쓰면 코드가 된다
| 2 bit | 염기 |
|---|---|
| 00 | A |
| 01 | C |
| 10 | G |
| 11 | T |
A→00 C→01 G→10 T→11
- 영문 1글자 = 8 bit = 2 bit × 4 = 4 nt · 이름 n글자 = 4n nt
- 한글 1글자 = UTF-8 3 byte = 24 bit = 12 nt
- 인코더 = ① 글자→비트 ② 2 bit씩 자르기 ③ 표 찾기 · 디코더 = 역순 + 역표(A→00 …)
- 표를 바꾸면 규칙이 바뀜(00→T로 하면 전혀 다른 서열) · 표를 모르면 읽을 수 없음 (지난주 다보스 사례)
- 제약 검사는 인코딩 "뒤"에 — GC%와 최대 연속 길이를 세어 판정
지난주 과제 ①에서 손으로 만든 서열을 꺼내 두기 — 곧 코드 결과와 한 글자씩 비교
이름 인코딩
Encode Your Name — Python, then MATLAB
- ① 이름 → 비트 ② 비트 → DNA ③ GC% · 호모폴리머
- ④ DNA → 이름 (디코딩) ⑤ FASTA 출력
- Python 10분 → MATLAB 10분
Python — 이름 인코딩 ①②③ (5분)
셀 3개 · 각 셀 실행 후 출력 확인
# 셀 1: 이름 → 비트
name = "SONG" # ← 자기 이름
bits = "".join(format(ord(c), "08b") for c in name)
print(bits, len(bits))
# 셀 2: 비트 → DNA (2 bits/nt)
m = {"00": "A", "01": "C", "10": "G", "11": "T"}
dna = "".join(m[bits[i:i+2]] for i in range(0, len(bits), 2))
print(dna, len(dna))
# 셀 3: GC% · 최대 호모폴리머
gc = (dna.count("G") + dna.count("C")) / len(dna) * 100
run, maxrun = 1, 1
for i in range(1, len(dna)):
run = run + 1 if dna[i] == dna[i-1] else 1
maxrun = max(maxrun, run)
print(f"GC={gc:.1f}% maxrun={maxrun}")01010011010011110100111001000111 32 CCATCATTCATGCACT 16 GC=43.8% maxrun=2
- 셀 2: range(0, 32, 2) → 0,2,4,… · bits[i:i+2]가 2 bit 조각 · m[…]이 표 찾기
- 셀 3: 앞 글자와 같으면 run+1, 다르면 1로 리셋 · 최댓값 기억
- 판정: GC 40~60%? maxrun ≤ 3? → 둘 다 통과하면 "합성 가능"
- 지난주 손 계산과 비교 — 다르면 어느 쪽이 틀렸는지 찾기
KeyError → bits 길이가 홀수(8의 배수인지 확인) · NameError → 앞 셀 미실행
Python — 디코딩 ④ · FASTA ⑤ (5분)
왕복 검증이 끝나야 인코더가 옳다
# 셀 4: DNA → 비트 → 이름 (디코딩)
inv = {v: k for k, v in m.items()} # 역표 A→00 …
bits2 = "".join(inv[b] for b in dna)
text = "".join(chr(int(bits2[i:i+8], 2))
for i in range(0, len(bits2), 8))
print(text, text == name)
# 셀 5: FASTA 형식으로 출력 · 파일 저장
fasta = f">{name}_2bit len={len(dna)} GC={gc:.1f}% maxrun={maxrun}\n{dna}\n"
print(fasta)
with open("my_name.fasta", "w") as f:
f.write(fasta)
# Colab 왼쪽 폴더 아이콘 → my_name.fasta 다운로드SONG True >SONG_2bit len=16 GC=43.8% maxrun=2 CCATCATTCATGCACT
- 역표는 표를 뒤집어 자동 생성 — 규칙을 두 번 쓰지 않음(실수 방지)
- text == name 이 True → 왕복 성공 · False면 인코더·디코더 중 하나가 틀림
- FASTA: ">" 헤더 한 줄 + 서열 한 줄 · 합성 주문·시퀀싱 분석의 공용 형식
- 헤더에 GC·maxrun을 적어 두면 나중에 pool 전체 통계를 낼 때 편함
my_name.fasta (과제 ①) · 다음 주 ECC 실습의 입력 파일
MATLAB — 이름 인코딩 ①②③ (5분)
같은 다섯 단계를 행렬 표기로
%% 1: 이름 → 비트
name = 'SONG'; % ← 자기 이름
bits = reshape(dec2bin(double(name), 8)', 1, []);
disp(bits); disp(length(bits))
%% 2: 비트 → DNA (2 bits/nt)
map = 'ACGT'; % 00→A 01→C 10→G 11→T
pairs = reshape(bits, 2, [])'; % 16×2 문자 행렬
idx = bin2dec(pairs); % 0..3
dna = map(idx + 1) % 1부터 세므로 +1
%% 3: GC% · 최대 호모폴리머
gc = sum(dna == 'G' | dna == 'C') / length(dna) * 100;
runs = diff([0, find(diff(double(dna)) ~= 0), length(dna)]);
maxrun = max(runs);
fprintf('GC=%.1f%% maxrun=%d\n', gc, maxrun)01010011010011110100111001000111
32
dna = 'CCATCATTCATGCACT'
GC=43.8% maxrun=2- reshape(bits, 2, [])' → 2 bit씩 한 행 · bin2dec가 행마다 숫자로
- map(idx+1) — 딕셔너리 대신 "문자열 인덱싱" · MATLAB다운 매핑표
- diff(double(dna)) ≠ 0 인 곳 = 염기가 바뀌는 곳 → 그 간격이 연속 길이
- Python 결과와 글자 하나까지 같아야 함
전치(')를 빼먹어 비트 순서가 섞임 · map(idx)로 써서 0 인덱스 오류
MATLAB — 디코딩 ④ · FASTA ⑤ (5분)
ismember로 역표를 만든다
%% 4: DNA → 비트 → 이름 (디코딩)
[~, idx2] = ismember(dna, map); % A→1 C→2 G→3 T→4
bits2 = reshape(dec2bin(idx2 - 1, 2)', 1, []);
text = char(bin2dec(reshape(bits2, 8, [])'))';
disp(text); disp(strcmp(text, name))
%% 5: FASTA 출력 · 파일 저장
fid = fopen('my_name.fasta', 'w');
fprintf(fid, '>%s_2bit len=%d GC=%.1f%% maxrun=%d\n%s\n', ...
name, length(dna), gc, maxrun, dna);
fclose(fid);
type my_name.fasta % 파일 내용 확인
% MATLAB Drive에서 my_name.fasta 다운로드SONG
1
>SONG_2bit len=16 GC=43.8% maxrun=2
CCATCATTCATGCACT- ismember(dna, map) → 각 염기가 map의 몇 번째인지(1~4) · −1 하면 0~3
- reshape(bits2, 8, [])' → 8 bit씩 한 행 · bin2dec → 숫자 → char
- strcmp가 1(true)이면 왕복 성공
- fprintf(fid, …)는 화면 대신 파일에 · type으로 확인
Python·MATLAB 두 FASTA 파일이 완전히 같은가? 다르면 어디서?
확장 · 퀴즈 · 과제
Beyond the Basics
- 제약을 위반했다면 — 1 bit/nt로 바꾸기
- 한글 이름 — UTF-8
- 결과표 작성 · 과제
확장 ① — 제약 위반 시: 1 bit/nt (Church 방식)로
규칙을 바꾸면 서열이 바뀐다
dna1 = ""
for i, b in enumerate(bits):
if b == "0":
dna1 += "A" if i % 2 == 0 else "C"
else:
dna1 += "G" if i % 2 == 0 else "T"
print(dna1, len(dna1))
# ATATACGTATACGTGTATACGTGCATACATGT 32dna1 = blanks(length(bits));
for i = 1:length(bits)
if bits(i) == '0'
if mod(i,2)==1, dna1(i)='A'; else, dna1(i)='C'; end
else
if mod(i,2)==1, dna1(i)='G'; else, dna1(i)='T'; end
end
end
disp(dna1)- 길이 2배(32 nt) · 대신 이웃 염기가 항상 다름 → 호모폴리머 길이 1 보장
- Python i%2==0(0부터) ↔ MATLAB mod(i,2)==1(1부터) — 같은 "짝수 자리" 조건
- GC%는 50% 근처로 수렴(0/1이 반반이면) · 밀도 1.0 bits/nt가 대가
- 다음 단계(4강): 길이를 늘리지 않고 오류까지 고치는 ECC
확장 ② — 한글 이름: UTF-8 3 byte
"송영준" = 9 byte = 72 bit = 36 nt
name = "송영준"
raw = name.encode("utf-8") # b'\xec\x86\xa1...' 9 byte
bits = "".join(format(b, "08b") for b in raw)
dna = "".join(m[bits[i:i+2]] for i in range(0, len(bits), 2))
print(len(raw), len(dna)) # 9 36
# 디코딩: 비트 → 바이트 → 문자열
bits2 = "".join(inv[b] for b in dna) # 새 dna로 셀 ④ 다시
b2 = bytes(int(bits2[i:i+8], 2) for i in range(0, len(bits2), 8))
print(b2.decode("utf-8"))name = '송영준'; raw = unicode2native(name, 'UTF-8'); % 1×9 uint8 bits = reshape(dec2bin(raw, 8)', 1, []); pairs = reshape(bits, 2, [])'; dna = map(bin2dec(pairs) + 1); disp([numel(raw) length(dna)]) % 9 36 % 디코딩 [~, idx2] = ismember(dna, map); % 새 dna로 %% 4 다시 bits2 = reshape(dec2bin(idx2 - 1, 2)', 1, []); b2 = uint8(bin2dec(reshape(bits2, 8, [])'))'; disp(native2unicode(b2, 'UTF-8'))
- ord(c)는 유니코드 번호(송 = 0xC1A1 = 49,569)라 8 bit에 안 들어감 → 바이트로 먼저 바꿈(encode / unicode2native)
- 디코딩도 바이트 단위로 되돌린 뒤 문자열로 (decode / native2unicode)
- 영문 4글자 16 nt vs 한글 3글자 36 nt — 같은 "이름"인데 2.25배 · 문자 체계도 저장 비용
확장 ③ (선택) — 더 해 보고 싶다면: 도구와 라이브러리
장난감에서 연구용까지
| 도구 | 무엇 | 용도 · 주의 |
|---|---|---|
| CacheSleuth DNA Translator | 웹 · 텍스트 ↔ 3문자 DNA 코드 (cachesleuth.com/dnacode.html) | 5분 감 잡기용 장난감 · 제약·ECC·인덱스 없음 → "진짜 스킴과 뭐가 다른가" 토론 |
| Chamaeleo (Python) | pip install Chamaeleo · BGI-Research | Simple·Church·Goldman·Grass·Blawat·DNA Fountain·Yin-Yang 전부 내장 → 6개 스킴 길이·GC·호모폴리머 비교 |
| TeamErlich/dna-fountain | GitHub · DNA Fountain 원저자 공식 구현 | 기프트카드를 푼 바로 그 코드 · 5강에서 사용 |
| Storage-D (iMeta 2024) | 여러 코덱을 고르는 웹 플랫폼 · GitHub DNAstorage-iSynBio | 학생 60명 교육 실험 사례 포함 |
| GNU Octave | MATLAB 호환 무료 로컬 프로그램 (octave.org) | 오늘 MATLAB 코드 전부 동작 · MATLAB Online 시간 소진 시 대안 |
!pip install Chamaeleo from Chamaeleo.methods.fixed import Church, Goldman, Blawat # 같은 비트열을 세 코덱에 넣고 길이 · GC% · maxrun을 표로 비교해 보기 (심화 과제 ③의 대안)
퀴즈 — 실습 확인
코드를 보지 않고 답하기
- bits[0:8]과 bits(1:8)이 같은 것을 가리키는 이유는? 그렇다면 bits[8:16]에 대응하는 MATLAB 표기는?힌트 — 0/1 인덱스 · 끝 포함 여부 · bits(9:16)
- 2 bits/nt 인코딩에서 이름이 "AAAA"를 만들려면 어떤 비트열이 필요한가? 어떤 글자가 그것을 만드는가?힌트 — 00000000 = 0 = NUL 문자 · 일반 글자에서는 최대 몇 개의 0이 이어질 수 있는가
- 디코딩 결과가 원본과 다를 때 의심할 곳 세 군데는?힌트 — 역표 · 조각 길이(2/8) · 비트 순서(전치)
오늘의 정리와 과제
3주차-2강
- Colab·MATLAB Online — 설치 없이 브라우저에서 코드 실행·저장
- 도구 6개(ord/chr · format/int · 자르기 · 붙이기)와 그 MATLAB 대응
- 인코더 = 글자→비트 → 2 bit 자르기 → 표 찾기 · 디코더 = 역순 + 역표
- GC%·최대 호모폴리머는 인코딩 "뒤"에 검사 · 위반 시 규칙 변경(1 bit/nt 등)
- 왕복 검증(text == name)이 True여야 인코더가 옳음 · 출력은 FASTA
- 한글은 UTF-8 3 byte → 바이트 단계를 거쳐야 함
① my_name.fasta (Python·MATLAB 각 1개, 한글·영문 이름 모두) ② 결과표: 이름 · 방식(2 bits/nt, 1 bit/nt) · 길이 · GC% · maxrun · 통과 여부 ③ 도전: Goldman 3진 회전을 코드로 구현해 지난주 손 계산과 비교
오류정정 코드(ECC) — 오늘 만든 my_name.fasta에 패리티·Reed–Solomon·XOR을 얹어 "일부러 망가뜨려도" 복원되게 만든다
Q and A
"코드는 규칙의 기록이다 — 규칙이 정확하면 코드는 20줄이면 된다."
송영준 Youngjun Song, Ph.D. · 인천대학교