Pandas로 실험 데이터 정리하기
이 토픽을 마치면
Pandas DataFrame으로 CSV를 읽고, 조건부 필터링을 하고, 그룹별 통계를 구하고, 두 테이블을 합칠 수 있습니다.
Pandas란
앞 토픽에서 csv 모듈로 파일을 읽고 반복문으로 처리하는 법을 배웠습니다. 시료 10개면 충분하지만, 시료가 5,000개이고 컬럼이 50개라면 반복문으로는 힘듭니다.
Pandas는 표 형태 데이터를 다루는 Python 라이브러리입니다. 엑셀 시트와 비슷한 DataFrame 구조에 데이터를 넣고, 필터링/정렬/집계를 한 줄로 처리합니다.
실험 노트에서 비유하면 — csv 모듈이 노트를 한 줄씩 읽는 것이라면, Pandas는 전체 표를 한눈에 펼쳐놓고 원하는 열/행을 즉시 꺼내는 것입니다.
DataFrame 만들기
import pandas as pd
df = pd.read_csv("expression_data.csv")
print(df)print(f"\n행: {len(df)}, 열: {len(df.columns)}")print(f"컬럼: {list(df.columns)}")pd.read_csv() 한 줄로 CSV 파일이 DataFrame이 됩니다. csv.DictReader와 반복문 없이도 전체 데이터가 로드됩니다.
직접 데이터를 만들 수도 있습니다:
import pandas as pd
data = { "sample_id": ["S001", "S002", "S003", "S004", "S005"], "gene": ["EGFR", "TP53", "EGFR", "BRCA1", "TP53"], "expression": [12.5, 3.2, 18.7, 7.1, 2.8], "status": ["high", "low", "high", "medium", "low"],}
df = pd.DataFrame(data)print(df)출력:
sample_id gene expression status
0 S001 EGFR 12.5 high
1 S002 TP53 3.2 low
2 S003 EGFR 18.7 high
3 S004 BRCA1 7.1 medium
4 S005 TP53 2.8 low데이터 살펴보기
DataFrame을 처음 받으면 전체 모습을 빠르게 파악합니다:
df.head() # 앞 5행df.tail(3) # 뒤 3행df.shape # (행 수, 열 수)df.dtypes # 각 열의 데이터 타입df.describe() # 숫자 열의 기본 통계 (평균, 표준편차, 최솟값, 최댓값)df.describe()는 실험 결과를 처음 받았을 때 "대충 어떤 범위인가?" 파악하는 데 유용합니다.
열/행 선택
# 열 하나 선택 — Series 반환genes = df["gene"]
# 열 여러 개 선택 — DataFrame 반환subset = df[["sample_id", "expression"]]
# 행 선택 — 인덱스로first_row = df.iloc[0] # 첫 번째 행first_three = df.iloc[0:3] # 0~2번째 행조건부 필터링
Pandas의 핵심 기능입니다. csv 모듈에서 for문 + if문으로 했던 것을 한 줄로 합니다:
# EGFR 유전자만egfr = df[df["gene"] == "EGFR"]print(egfr)
# expression이 5 이상인 시료high_expr = df[df["expression"] >= 5.0]print(high_expr)
# 복합 조건: EGFR이면서 expression이 15 이상egfr_high = df[(df["gene"] == "EGFR") & (df["expression"] >= 15.0)]print(egfr_high)
assert len(egfr) == 2assert len(egfr_high) == 1&는 AND, |는 OR입니다. 각 조건을 괄호로 감싸야 합니다.
정렬
# expression 내림차순sorted_df = df.sort_values("expression", ascending=False)print(sorted_df)그룹별 집계: groupby
"유전자별 평균 발현량은?" — 엑셀에서 피벗 테이블로 하던 것을 groupby로 합니다:
gene_stats = df.groupby("gene")["expression"].agg(["mean", "std", "count"])print(gene_stats)출력:
mean std count
gene
BRCA1 7.10 NaN 1
EGFR 15.60 4.384062 2
TP53 3.00 0.282843 2groupby("gene") — 유전자별로 묶고, ["expression"] — expression 열에 대해, agg(["mean", "std", "count"]) — 평균, 표준편차, 개수를 한번에 계산합니다.
새 열 추가
# expression을 log2로 변환한 열 추가import numpy as np
df["log2_expression"] = np.log2(df["expression"])print(df[["sample_id", "expression", "log2_expression"]])두 테이블 합치기: merge
실험 데이터와 시료 정보가 별도 파일에 있을 때:
import pandas as pd
# 시료 메타데이터metadata = pd.DataFrame({ "sample_id": ["S001", "S002", "S003", "S004", "S005"], "tissue": ["lung", "breast", "lung", "breast", "colon"], "age": [45, 62, 38, 55, 71],})
# expression 데이터와 합치기merged = pd.merge(df, metadata, on="sample_id")print(merged)on="sample_id" — 두 테이블에서 같은 값을 가진 행끼리 합칩니다. SQL의 JOIN과 같은 개념입니다. database-basics 토픽에서 배운 것이 여기서도 통합니다.
결과 저장
# CSV로 저장egfr.to_csv("egfr_samples.csv", index=False)
# TSV로 저장egfr.to_csv("egfr_samples.tsv", sep="\t", index=False)index=False — 행 번호(0, 1, 2...)를 파일에 포함하지 않습니다.
직접 해보기 (Faded Example)
아래 빈칸을 채워 DataFrame에서 조건부 필터링과 그룹 집계를 수행하세요.
import pandas as pddf = pd.read_csv("samples.csv")# OD가 1.0 이상인 시료 필터링passed = df[df["od"] 1.0]# 상태별 평균 ODstats = df.("status")["od"].mean()print(stats)
흔한 에러 & 해결법
Q: KeyError: 'gene' 에러가 납니다
컬럼 이름이 정확한지 확인하세요. df.columns로 실제 컬럼 이름을 출력해보세요. 공백이 포함되어 있을 수 있습니다 (" gene" vs "gene").
Q: 필터링했는데 빈 DataFrame이 나옵니다
조건이 너무 엄격하거나 데이터 타입이 맞지 않을 수 있습니다. df["expression"].dtype으로 타입을 확인하세요. 문자열을 숫자로 비교하면 결과가 없을 수 있습니다.
Q: SettingWithCopyWarning 경고가 뜹니다
필터링한 결과에 값을 할당할 때 나오는 경고입니다. df_filtered = df[조건].copy()로 명시적 복사본을 만든 뒤 수정하면 경고가 사라집니다.
Q: merge 했는데 행 수가 늘어났습니다
on으로 지정한 열에 중복 값이 있으면 모든 조합이 만들어져 행이 늘어납니다. merge 전에 양쪽 테이블에서 df["sample_id"].duplicated().sum()으로 중복을 확인하세요.