5 분 소요

이 장에서 배울 것

이번 장에서는 통계분석을 코드로 실행하는 방법을 배웁니다. 수학 부록에서 평균, p-value, 회귀, 다중검정을 배웠다면, 여기서는 그 생각을 파이썬 코드로 옮깁니다.

핵심 용어를 먼저 정리하겠습니다.

  • 사이파이(scipy): 과학 계산과 통계 검정을 위한 파이썬 라이브러리입니다.
  • 통계량(statistic): 검정에서 계산되는 숫자입니다. 예를 들어 t-test의 t값이 있습니다.
  • p값(p-value): 관찰한 차이가 우연으로도 나올 수 있는 정도를 나타내는 값입니다. 앞으로는 p-value라고도 쓰겠습니다.
  • t검정(t-test): 두 그룹 평균이 다른지 살펴보는 대표적인 검정입니다.
  • 상관분석(correlation analysis): 두 숫자 변수가 함께 움직이는 경향을 보는 분석입니다.
  • 회귀분석(regression analysis): 한 값이 다른 값과 어떻게 관련되는지 모델로 설명하는 분석입니다.
  • 다중검정 보정(multiple testing correction): 검정을 많이 할 때 거짓 양성을 줄이기 위한 보정입니다.

통계분석 코드

가장 쉬운 비유: 계산기 대신 검정 도구 쓰기

암 그룹과 정상 그룹의 유전자 발현량이 다르게 보인다고 합시다. 눈으로만 보면 “다른 것 같다”고 느낄 수 있습니다. 하지만 연구에서는 “얼마나 그럴듯한 차이인지”를 숫자로 확인해야 합니다.

파이썬 통계 코드는 이 질문을 던지는 도구입니다.

두 그룹이 정말 다른가?
이 관계가 우연일 가능성은 어느 정도인가?
너무 많은 검정을 해서 우연한 발견을 진짜처럼 보고 있지는 않은가?

t검정 실행하기

두 그룹 평균을 비교하는 가장 단순한 예를 봅시다.

from scipy.stats import ttest_ind

cancer = [10.2, 11.5, 9.8, 12.1]
normal = [5.2, 6.1, 4.9, 5.8]

stat, p = ttest_ind(cancer, normal)
print(stat)
print(p)

ttest_ind는 서로 독립인 두 그룹의 평균을 비교합니다. 결과로 통계량과 p-value가 나옵니다.

중요한 것은 p-value가 “차이의 크기”가 아니라는 점입니다. p-value는 “차이가 없다는 가정 아래에서 이런 결과가 얼마나 이상한가”를 보는 숫자입니다.

평균도 함께 확인하기

검정을 하기 전후에는 평균도 같이 봐야 합니다.

import numpy as np

print(np.mean(cancer))
print(np.mean(normal))

예를 들어 암 그룹 평균이 10.9이고 정상 그룹 평균이 5.5라면, 차이의 방향과 크기를 대략 볼 수 있습니다. p-value만 보고 해석하면 위험합니다.

상관분석 코드

두 숫자 변수가 함께 커지는지 보고 싶을 때 상관분석을 씁니다.

from scipy.stats import pearsonr

gene_a = [1, 2, 3, 4]
gene_b = [2, 4, 6, 8]

r, p = pearsonr(gene_a, gene_b)
print(r)
print(p)

r은 상관계수입니다. r이 1에 가까우면 두 값이 함께 커지는 경향이 강합니다. 그러나 상관관계는 원인관계를 뜻하지 않습니다.

간단한 회귀분석

회귀분석은 입력값으로 출력값을 설명하거나 예측하는 방법입니다. 여기서는 가장 간단한 선형회귀 느낌만 봅니다.

from scipy.stats import linregress

x = [1, 2, 3, 4]
y = [2, 4, 6, 8]

result = linregress(x, y)
print(result.slope)
print(result.intercept)

slope는 기울기입니다. 위 예시에서는 x가 1 증가할 때 y가 2 증가하므로 기울기는 2에 가깝습니다.

여러 유전자를 검정할 때

유전자 하나만 검정하면 문제가 단순합니다. 하지만 RNA-seq에서는 유전자 수천 개를 한 번에 검정합니다. 그러면 우연히 작게 나온 p-value가 섞일 수 있습니다.

그래서 다중검정 보정을 합니다.

from statsmodels.stats.multitest import multipletests

p_values = [0.001, 0.02, 0.20, 0.04]
reject, q_values, _, _ = multipletests(p_values, method="fdr_bh")

print(reject)
print(q_values)

여기서 q_values는 FDR 방식으로 보정한 값이라고 생각하면 됩니다. FDR은 거짓 발견 비율을 조절하는 방법입니다.

표 데이터와 함께 쓰기

실제 분석에서는 리스트만 쓰지 않고 판다스 표와 함께 사용합니다.

import pandas as pd
from scipy.stats import ttest_ind

df = pd.read_csv("expression.csv")

cancer = df[df["group"] == "cancer"]["expression"]
normal = df[df["group"] == "normal"]["expression"]

stat, p = ttest_ind(cancer, normal)
print(p)

이 코드는 표에서 암 그룹과 정상 그룹을 나눈 뒤 t검정을 실행합니다.

생물정보학에서 왜 중요한가

차등발현 분석, 변이 연관성 분석, 임상 변수 비교, 모델 성능 평가에는 통계 코드가 계속 등장합니다. 통계 코드를 쓸 때는 함수 이름만 외우면 안 됩니다. 어떤 질문에 어떤 검정을 쓰는지, 결과 숫자가 무엇을 뜻하는지 함께 이해해야 합니다.

계산 감각: p-value와 효과 크기를 함께 보기

통계 코드는 숫자를 내지만, 숫자 하나만으로 결론을 내리면 위험합니다. 예를 들어 암 그룹 평균이 10.8이고 정상 그룹 평균이 10.1인데 p-value가 작을 수 있습니다. 샘플 수가 매우 크면 작은 차이도 통계적으로 유의하게 나올 수 있기 때문입니다.

따라서 최소한 세 가지를 함께 봐야 합니다.

  • 평균 차이가 어느 방향인가
  • 차이의 크기가 생물학적으로 의미 있는가
  • 여러 유전자를 동시에 검정했다면 보정 후에도 남는가

RNA-seq에서는 유전자 수천 개를 동시에 검정하므로 우연히 작은 p-value가 많이 생깁니다. 그래서 FDR이나 q-value 같은 다중검정 보정 개념이 중요합니다.

초보자가 자주 하는 오해는 p-value를 “효과가 큰 정도”라고 생각하는 것입니다. p-value는 귀무가설 아래에서 현재처럼 극단적인 결과가 나올 가능성과 관련된 값이지, 유전자 발현 차이의 크기를 직접 말해주는 값이 아닙니다.

어려운 개념 보강: p-value, 효과크기, FDR을 한꺼번에 오해하지 않기

통계 코드에서 가장 위험한 습관은 p < 0.05만 보고 결과를 끝내는 것입니다. p-value는 중요하지만, p-value 하나가 생물학적 의미를 전부 말해 주지는 않습니다.

p-value는 다음 질문에 대한 답에 가깝습니다.

“두 그룹에 실제 차이가 없다고 가정했을 때,
지금 관찰한 정도의 차이 또는 그보다 더 극단적인 차이가 나올 가능성이 얼마나 되는가?”

여기서 중요한 기호와 말은 다음과 같습니다.

  • “차이가 없다고 가정”: 귀무가설이라고 부릅니다.
  • “관찰한 차이”: 실제 데이터에서 계산한 평균 차이, t값, 상관계수 같은 값입니다.
  • “더 극단적인 차이”: 우연만으로 보기 어려운 방향의 결과입니다.

p-value는 “이 유전자가 진짜 원인일 확률”이 아닙니다. p-value가 작다는 것은 “차이가 없다는 가정과 현재 데이터가 잘 맞지 않는다”는 뜻입니다.

그래서 평균 차이 또는 효과크기(effect size)를 같이 봐야 합니다. 예를 들어 두 유전자가 모두 p-value가 작더라도 실제 차이의 크기는 다를 수 있습니다.

import numpy as np

normal = np.array([10, 11, 9, 10])
cancer = np.array([20, 21, 19, 20])

mean_diff = np.mean(cancer) - np.mean(normal)
print(mean_diff)  # 10.0

위 예시에서 평균 차이는 10입니다. 이 값은 방향과 크기를 알려 줍니다. p-value는 우연 가능성의 신호이고, 평균 차이는 실제 변화량의 감각입니다. 둘을 같이 봐야 합니다.

유전자 수천 개를 한꺼번에 검정하면 더 큰 문제가 생깁니다. 검정을 많이 할수록 우연히 작게 나온 p-value가 섞입니다. 그래서 다중검정 보정이 필요합니다. FDR(false discovery rate)은 “발견했다고 부른 것들 중 거짓 발견의 비율을 조절하려는 기준”입니다.

Benjamini-Hochberg 방식의 아주 단순한 감각은 다음과 같습니다.

보정 p-value의 감각 ≈ 원래 p-value × 전체 검정 수 / 순위

예를 들어 1000개 유전자를 검정했고, 어떤 유전자의 p-value가 0.001이며 작은 순서로 10번째라면 다음처럼 생각할 수 있습니다.

0.001 × 1000 / 10 = 0.1

이 계산은 실제 구현의 모든 세부 규칙을 다 담지는 않지만, 왜 보정 후 p-value가 원래 p-value보다 커질 수 있는지 직관을 줍니다. 생물정보학에서는 “유전자 하나”보다 “유전자 수천 개”를 동시에 보는 일이 많으므로, 원래 p-value와 FDR을 구분해야 합니다.

흔한 오해는 세 가지입니다. 첫째, p-value는 효과크기가 아닙니다. 둘째, p-value가 작다고 생물학적으로 반드시 중요하다는 뜻은 아닙니다. 셋째, FDR 보정 없이 수천 개 결과에서 마음에 드는 유전자만 고르면 우연한 발견을 진짜처럼 해석할 위험이 큽니다.

미니 실습 블록: p-value와 효과크기를 함께 보기

이 실습은 p-value와 효과크기를 함께 보기를 직접 손으로 확인하는 연습입니다. 왜 필요한가 하면, 통계적으로 유의해도 변화가 너무 작거나, 변화가 커도 불확실하면 해석이 달라지기 때문입니다.

import pandas as pd

result = pd.DataFrame({
    "gene": ["A", "B", "C"],
    "log2FC": [2.0, 0.1, -1.5],
    "padj": [0.001, 0.0001, 0.20]
})

selected = result[(result["padj"] < 0.05) & (result["log2FC"].abs() >= 1)]
print(selected)

각 코드 요소의 의미를 풀어보면 다음과 같습니다. &는 두 조건을 모두 만족한다는 뜻이고, abs()는 절댓값입니다. gene A만 통계 신호와 변화 크기를 함께 만족합니다.

생물정보학/계산생물학에서 쓰이는 장면은 분명합니다. RNA-seq 차등발현 결과에서 후보 유전자를 고를 때 씁니다.

흔한 오해 또는 주의점도 있습니다. p-value가 작다고 바로 원인 유전자라고 말하면 안 됩니다. 통계적 연관은 원인 증명과 다릅니다.

핵심 정리

통계분석 코드는 생물학적 질문을 숫자로 확인하는 도구입니다. ttest_ind는 두 그룹 평균 비교, pearsonr은 상관분석, linregress는 간단한 선형회귀, multipletests는 다중검정 보정에 사용됩니다. p-value는 차이의 크기가 아니므로 평균, 효과 크기, 생물학적 의미를 함께 봐야 합니다.

문제 풀이

통계분석 코드

0 / 40
Gemini AI 채점

주관식 답안은 Gemini API로 채점합니다. API 키는 이 브라우저에만 저장됩니다.

API KEY 미등록
  1. 1. [쉬움] 객관식

    scipy의 역할로 가장 적절한 것은?

    선택지
  2. 2. [코드] 객관식

    from scipy.stats import ttest_ind는 무엇을 가져오는 코드인가?

    선택지
  3. 3. [보통] 객관식

    t검정(t-test)의 주된 용도는?

    선택지
  4. 4. [보통] 객관식

    p-value의 설명으로 적절한 것은?

    선택지
  5. 5. [코드] 객관식

    np.mean(cancer)는 무엇을 계산하는가?

    선택지
  6. 6. [코드] 객관식

    pearsonr(gene_a, gene_b)는 무엇을 계산하는가?

    선택지
  7. 7. [보통] 객관식

    상관계수 r이 1에 가까울 때 의미로 적절한 것은?

    선택지
  8. 8. [보통] 객관식

    linregress(x, y)에서 slope는 무엇인가?

    선택지
  9. 9. [계산] 객관식

    x=[1,2,3,4], y=[2,4,6,8]의 선형 관계에서 기울기는?

    선택지
  10. 10. [코드] 객관식

    multipletests(p_values, method="fdr_bh")의 역할로 적절한 것은?

    선택지
  11. 11. [보통] 객관식

    다중검정 보정이 필요한 이유는?

    선택지
  12. 12. [코드] 객관식

    df[df["group"] == "cancer"]["expression"]의 의미는?

    선택지
  13. 13. [보통] 객관식

    p-value만 보고 해석하면 위험한 이유는?

    선택지
  14. 14. [계산] 객관식

    두 그룹 평균이 cancer=10.9, normal=5.5라면 평균 차이는?

    선택지
  15. 15. [코드] 객관식

    stat, p = ttest_ind(cancer, normal)에서 p에는 무엇이 들어가는가?

    선택지
  16. 16. [보통] 객관식

    상관관계 해석에서 조심해야 할 점은?

    선택지
  17. 17. [쉬움] 객관식

    p_values=[0.001,0.02,0.20,0.04]는 무엇을 담은 리스트인가?

    선택지
  18. 18. [보통] 객관식

    통계분석 코드에서 그룹을 먼저 나누는 이유는?

    선택지
  19. 19. [보통] 객관식

    reject가 다중검정 결과에서 보통 뜻하는 것은?

    선택지
  20. 20. [보통] 객관식

    통계 코드가 생물정보학에서 중요한 이유는?

    선택지
  21. 21. [중간] 객관식

    암 그룹 평균이 12, 정상 그룹 평균이 7이면 평균 차이(cancer-normal)는?

    선택지
  22. 22. [중간] 객관식

    p-value 해석으로 가장 적절한 것은?

    선택지
  23. 23. [중간] 객관식

    RNA-seq에서 수천 개 유전자를 동시에 검정할 때 필요한 절차는?

    선택지
  24. 24. [중간] 객관식

    상관계수 r이 1에 가까울 때 의미로 가장 적절한 것은?

    선택지
  25. 25. [중간] 객관식

    선형회귀에서 기울기 slope가 2라는 말의 의미로 가장 적절한 것은?

    선택지
  26. 26. [중간] 객관식

    p-values [0.001, 0.02, 0.20, 0.04]에서 가장 작은 p-value는?

    선택지
  27. 27. [중간] 객관식

    통계적으로 유의하지만 생물학적으로 중요하지 않을 수 있는 이유로 맞는 것은?

    선택지
  28. 28. [중간] 객관식

    두 그룹 평균 비교에 흔히 쓰이는 scipy 함수는?

    선택지
  29. 29. [실전] 객관식

    padj < 0.05abs(log2FC) >= 1을 함께 쓰는 이유는?

    선택지
  30. 30. [실전] 객관식

    log2FC = -1은 대략 어떤 의미인가?

    선택지
  31. 주관식 31. [실습] 주관식 · Gemini 채점

    두 리스트 cancer, normal에 대해 t검정을 실행하고 p-value를 출력하는 코드를 작성하라.

  32. 주관식 32. [실습] 주관식 · Gemini 채점

    두 리스트의 평균을 각각 출력하는 코드를 작성하라.

  33. 주관식 33. [실습] 주관식 · Gemini 채점

    pearsonr(gene_a, gene_b) 결과의 rp가 각각 무엇을 뜻하는지 설명하라.

  34. 주관식 34. [실습] 주관식 · Gemini 채점

    다중검정 보정이 왜 필요한지 RNA-seq 예시로 설명하라.

  35. 주관식 35. [실습] 주관식 · Gemini 채점

    판다스 데이터프레임에서 cancer 그룹과 normal 그룹의 expression을 나누는 코드를 작성하라.

  36. 주관식 36. [실습] 주관식 · Gemini 채점

    p-value만으로 결론 내리면 안 되는 이유를 설명하라.

  37. 주관식 37. [실습] 주관식 · Gemini 채점

    p-value, 평균 차이, 다중검정 보정을 함께 봐야 하는 이유를 RNA-seq 예시로 설명하라.

  38. 주관식 38. [실습] 주관식 · Gemini 채점

    두 그룹 리스트 cancer=[10,12,14], normal=[4,5,6]의 평균 차이를 계산하는 간단한 파이썬 코드를 작성하라.

  39. 주관식 39. [실습] 주관식 · Gemini 채점

    result DataFrame에서 padj < 0.05인 행만 고르는 코드를 작성하라.

  40. 주관식 40. [실습] 주관식 · Gemini 채점

    log2FC = 2log2FC = -2를 배수 변화로 해석하라.