Pandasで実験データを整理する
このトピックを終えたら
Pandas DataFrameでCSVを読み込み、条件付きフィルタリングを行い、グループ別統計を求め、2つのテーブルを結合できるようになります。
Pandasとは
前のトピックでcsvモジュールでファイルを読んでループで処理する方法を学びました。サンプル10個なら十分ですが、サンプルが5,000個でカラムが50個となるとループでは大変です。
Pandasは表形式データを扱うPythonライブラリです。Excelシートに似たDataFrame構造にデータを入れて、フィルタリング/ソート/集計を1行で処理します。
実験ノートで例えると — csvモジュールがノートを1行ずつ読むのに対して、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()1行で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()は実験結果を初めて受け取った時、「大体どんな範囲か?」を把握するのに便利です。
列/行の選択
# 列を1つ選択 — 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文でやっていたことを1行にまとめます:
# EGFR遺伝子のみegfr = df[df["gene"] == "EGFR"]print(egfr)
# expressionが5以上のサンプルhigh_expr = df[df["expression"] >= 5.0]print(high_expr)
# 複合条件:EGFRかつexpression >= 15egfr_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
「遺伝子別の平均発現量は?」— Excelでピボットテーブルでやっていたことを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"]])2つのテーブルを結合する: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()で重複を確認してください。