19강 / 20강

GSEA ② — 우연히도 이만한 점수가 나올까

순열검정으로 만드는 귀무분포, 그리고 NES

GSEA ② — 우연히도 이만한 점수가 나올까
무작위로 뽑은 가짜 팀 1,000개와 견주어 보면, 진짜 팀의 점수가 특별한지 알 수 있습니다.

EMT 팀의 ES 가 −0.58 이 나왔습니다. 이게 큰 값인가요?

말할 수 없습니다. 비교할 기준이 없으니까요. 33명을 아무렇게나 뽑아도 어느 정도의 ES 는 나옵니다. 그래서 물어야 할 진짜 질문은 이것입니다 —

아무 유전자나 33개 뽑았을 때 나오는 ES 와 비교하면 −0.58 은 얼마나 특별한가?

답을 구하는 방법은 소박합니다. 실제로 아무 유전자나 33개 뽑아 봅니다. 1,000번. 이것을 순열검정(permutation test) 이라고 합니다.

rng = np.random.default_rng(seed) null = np.array([es(rng.choice(N, len(hits), replace=False)) for _ in range(nperm)])

rng.choice(N, k, replace=False) — 0부터 N−1 중에서 중복 없이 k 개를 무작위로 고릅니다. 같은 크기의 가짜 세트를 1,000개 만들어 각각의 ES 를 구하면, "우연히 나올 수 있는 ES 의 분포" 가 만들어집니다. 이것을 귀무분포 라고 해요.

NES — 눈금 맞추기

마지막 한 단계가 남았습니다. ES 를 그대로 쓰면 세트끼리 비교할 수 없습니다. 세트 크기가 다르면 우연히 나오는 ES 의 크기도 달라지기 때문이에요 (작은 세트일수록 극단적인 값이 잘 나옵니다).

same = null[null >= 0] if e >= 0 else null[null < 0] mu = np.abs(same).mean() return e / (mu + 1e-9) # NES = 내 ES ÷ 같은 방향 우연 ES 의 평균 크기

NES(Normalized Enrichment Score)는 "우연히 나오는 크기의 몇 배인가"입니다. 그래서 세트 크기와 무관하게 비교할 수 있어요.

부호가 같은 것끼리만 비교하는 null[null >= 0] if e >= 0 else ... 도 중요합니다. 양수 ES 와 음수 ES 는 분포 모양이 다르기 때문에, 내 ES 와 같은 방향의 우연 값들과 견주어야 공정합니다.

순열검정을 처음부터 끝까지 눈으로 봅니다. 300번만 돌려도 그림이 보입니다.
Python 브라우저에서 실행

🎮 귀무분포와 견주어 보기

가짜 팀들의 ES 분포(히스토그램) 위에 진짜 ES 를 세워 봅니다. 순열 횟수를 늘리면 분포가 어떻게 매끄러워지는지도 보세요.

순열검정 실험실 — 가짜 팀 1,000개와 진짜 팀 견주기
불러오는 중…
네 경로 중 셋이 뚜렷한 음수. 이 결과를 20강에서 채점합니다. (30초쯤 걸립니다)
Python 브라우저에서 실행
🤔 확인 문제
rng.choice(N, k, replace=False) 에서 replace=False 는?
🤔 확인 문제
NES 가 ES 대신 필요한 이유는?
🤔 확인 문제
np.random.default_rng(0) 처럼 seed 를 고정하는 이유는?