機械学習で遺伝子発現データを分析する
このトピックを終えたら
scikit-learnで遺伝子発現データをクラスタリング(K-means)し、PCAで次元を圧縮し、腫瘍/正常サンプルを分類できるようになります。
機械学習とは?
実験で経験を積むと、ゲル写真を見ただけで「このバンドパターンはmutationだ」と判断できるようになりますよね? 機械学習はコンピュータにこの「パターン認識」能力をデータで学習させることです。
バイオでよく使うML:
- 教師なし学習: ラベルなしでデータのパターンを見つける(クラスタリング、PCA)
- 教師あり学習: 正解のあるデータで分類モデルを学習(腫瘍 vs 正常)
データ準備:仮想遺伝子発現データ
実際のRNA-seqデータを模した仮想データを作ります。100サンプル(正常50 + 腫瘍50)で20遺伝子の発現量を測定した状況です。
import numpy as npimport pandas as pd
np.random.seed(42)
n_samples = 100n_genes = 20gene_names = [f"Gene_{i+1:02d}" for i in range(n_genes)]sample_labels = ["Normal"] * 50 + ["Tumor"] * 50
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0tumor_expr[:, :5] += 3.0 # Gene_01~05 上方制御tumor_expr[:, 15:] -= 2.0 # Gene_16~20 下方制御
expression = np.vstack([normal_expr, tumor_expr])df = pd.DataFrame(expression, columns=gene_names)df["Label"] = sample_labels
print(f"データサイズ: {df.shape}")print(f"サンプル: Normal {sum(df['Label']=='Normal')}, Tumor {sum(df['Label']=='Tumor')}")print(f"\n最初の5行:")print(df.head().to_string())
assert df.shape == (100, 21)assert sum(df["Label"] == "Normal") == 50assert sum(df["Label"] == "Tumor") == 50PCA:高次元データを2Dに圧縮
20遺伝子の発現量を人が見れる2次元に圧縮します。PCA(主成分分析)はデータの分散が最も大きい方向を見つけます。
import numpy as npimport pandas as pdfrom sklearn.preprocessing import StandardScalerfrom sklearn.decomposition import PCA
np.random.seed(42)n_genes = 20gene_names = [f"Gene_{i+1:02d}" for i in range(n_genes)]
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0tumor_expr[:, :5] += 3.0tumor_expr[:, 15:] -= 2.0
expression = np.vstack([normal_expr, tumor_expr])labels = ["Normal"] * 50 + ["Tumor"] * 50
# 1. 標準化(平均0、分散1)scaler = StandardScaler()X_scaled = scaler.fit_transform(expression)
# 2. PCAを適用pca = PCA(n_components=2)X_pca = pca.fit_transform(X_scaled)
print(f"元の次元: {expression.shape[1]}個の遺伝子")print(f"圧縮後: {X_pca.shape[1]}個の主成分")print(f"説明分散: PC1={pca.explained_variance_ratio_[0]:.1%}, PC2={pca.explained_variance_ratio_[1]:.1%}")print(f"合計: {sum(pca.explained_variance_ratio_):.1%}")
assert X_pca.shape == (100, 2)assert pca.explained_variance_ratio_[0] > pca.explained_variance_ratio_[1]import matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltimport numpy as npfrom sklearn.preprocessing import StandardScalerfrom sklearn.decomposition import PCA
np.random.seed(42)n_genes = 20
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0tumor_expr[:, :5] += 3.0tumor_expr[:, 15:] -= 2.0
expression = np.vstack([normal_expr, tumor_expr])labels = np.array(["Normal"] * 50 + ["Tumor"] * 50)
scaler = StandardScaler()X_scaled = scaler.fit_transform(expression)pca = PCA(n_components=2)X_pca = pca.fit_transform(X_scaled)
fig, ax = plt.subplots(figsize=(8, 6))for label, color in [("Normal", "#2E86AB"), ("Tumor", "#E74C3C")]: mask = labels == label ax.scatter(X_pca[mask, 0], X_pca[mask, 1], c=color, s=40, alpha=0.7, label=label, edgecolors="white", linewidth=0.5)
ax.set_xlabel(f"PC1 ({pca.explained_variance_ratio_[0]:.1%})", fontsize=12)ax.set_ylabel(f"PC2 ({pca.explained_variance_ratio_[1]:.1%})", fontsize=12)ax.set_title("PCA — Normal vs Tumor", fontsize=14, fontweight="bold")ax.legend(fontsize=11)ax.grid(True, alpha=0.2)fig.tight_layout()fig.savefig("pca_plot.png", dpi=150)plt.close(fig)
print("pca_plot.png 保存完了")assert X_pca.shape == (100, 2)K-meansクラスタリング:教師なし学習
ラベルなしでデータだけからグループを見つけます。K-meansは最も直感的なクラスタリングアルゴリズムです。
import numpy as npfrom sklearn.preprocessing import StandardScalerfrom sklearn.cluster import KMeans
np.random.seed(42)n_genes = 20
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0tumor_expr[:, :5] += 3.0tumor_expr[:, 15:] -= 2.0
expression = np.vstack([normal_expr, tumor_expr])true_labels = [0] * 50 + [1] * 50
scaler = StandardScaler()X_scaled = scaler.fit_transform(expression)
# K-means: 2つのクラスタに分類kmeans = KMeans(n_clusters=2, random_state=42, n_init=10)clusters = kmeans.fit_predict(X_scaled)
# クラスタが実際のラベルとどれくらい一致するか確認from sklearn.metrics import adjusted_rand_scoreari = adjusted_rand_score(true_labels, clusters)print(f"クラスタ 0: {sum(clusters == 0)}サンプル")print(f"クラスタ 1: {sum(clusters == 1)}サンプル")print(f"Adjusted Rand Index: {ari:.3f} (1.0 = 完全一致)")
assert len(set(clusters)) == 2assert ari > 0.5print("K-meansクラスタリングが正常/腫瘍の区別に成功")分類:教師あり学習
今度は正解(Normal/Tumor)がわかっているデータでモデルを学習し、新しいサンプルを分類します。
import numpy as npfrom sklearn.preprocessing import StandardScalerfrom sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.metrics import accuracy_score, classification_report
np.random.seed(42)n_genes = 20
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0tumor_expr[:, :5] += 3.0tumor_expr[:, 15:] -= 2.0
X = np.vstack([normal_expr, tumor_expr])y = np.array([0] * 50 + [1] * 50) # 0=Normal, 1=Tumor
# 1. データ分割(80%学習、20%テスト)X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 2. 標準化scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)
# 3. Random Forest学習clf = RandomForestClassifier(n_estimators=100, random_state=42)clf.fit(X_train_scaled, y_train)
# 4. 予測と評価y_pred = clf.predict(X_test_scaled)accuracy = accuracy_score(y_test, y_pred)
print(f"精度: {accuracy:.1%}")print(f"\n学習データ: {len(X_train)}個、テストデータ: {len(X_test)}個")print(f"\n分類レポート:")print(classification_report(y_test, y_pred, target_names=["Normal", "Tumor"]))
assert accuracy > 0.8assert len(X_train) == 80assert len(X_test) == 20Feature Importance:どの遺伝子が重要か?
Random Forestは分類に最も貢献した遺伝子(feature)を教えてくれます。
import matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltimport numpy as npfrom sklearn.preprocessing import StandardScalerfrom sklearn.ensemble import RandomForestClassifier
np.random.seed(42)n_genes = 20gene_names = [f"Gene_{i+1:02d}" for i in range(n_genes)]
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0tumor_expr[:, :5] += 3.0tumor_expr[:, 15:] -= 2.0
X = np.vstack([normal_expr, tumor_expr])y = np.array([0] * 50 + [1] * 50)
scaler = StandardScaler()X_scaled = scaler.fit_transform(X)clf = RandomForestClassifier(n_estimators=100, random_state=42)clf.fit(X_scaled, y)
importances = clf.feature_importances_sorted_idx = np.argsort(importances)[-10:] # 上位10個
fig, ax = plt.subplots(figsize=(8, 5))ax.barh(range(len(sorted_idx)), importances[sorted_idx], color="#2E86AB")ax.set_yticks(range(len(sorted_idx)))ax.set_yticklabels([gene_names[i] for i in sorted_idx], fontsize=10)ax.set_xlabel("Feature Importance", fontsize=12)ax.set_title("Top 10 Important Genes", fontsize=14, fontweight="bold")fig.tight_layout()fig.savefig("feature_importance.png", dpi=150)plt.close(fig)
top_gene = gene_names[sorted_idx[-1]]print(f"最も重要な遺伝子: {top_gene}")print(f"上位5遺伝子: {[gene_names[i] for i in sorted_idx[-5:]]}")
assert len(importances) == 20assert sum(importances) > 0.99やってみよう(Faded Example)
空欄を埋めて、scikit-learnのfit-predictパターンを完成させてください。
from sklearn.cluster import KMeansfrom sklearn.preprocessing import StandardScalerscaler = StandardScaler()X_scaled = scaler.fit_transform(data)kmeans = KMeans(n_clusters=)clusters = kmeans.(X_scaled)
よくあるエラーと解決法
Q: ValueError: could not convert string to float
文字列の列(例:遺伝子名、ラベル)がデータに含まれています。df.select_dtypes(include=[np.number])で数値列のみを選択してください。
Q: 精度が50%しかありません
データがランダムと変わらないということです。標準化(StandardScaler)をしたか、特徴量(遺伝子)が十分に異なるパターンを持っているか確認してください。
Q: ConvergenceWarning: Number of distinct clusters found
K-meansが収束しませんでした。n_init=10、max_iter=300を増やすか、データのスケーリングを確認してください。
おめでとうございます!DevBench共通 + A-1トラックのMVP 5トピックをすべて完了しました。