第 9 章 ソースコード一覧¶
書籍第 9 章「教師なし学習」に掲載しているソースコードの一覧です。番号は書籍の「入力」番号と対応しています。上のバッジから、この章のコードをまとめた Google Colab ノートブックを開けます。
入力 9.1: 合成データへのk-means法の適用¶
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
# 3つのクラスタからなる合成データを生成
X, y_true = make_blobs(n_samples=300, centers=3,
cluster_std=0.80, random_state=0)
# k-means法を適用(k=3)
kmeans = KMeans(n_clusters=3, random_state=0, n_init=10)
kmeans.fit(X)
y_pred = kmeans.labels_
# 結果を可視化
fig, axes = plt.subplots(1, 2, figsize=(5.8, 2.4))
# 左:元データ(ラベルなし)
axes[0].scatter(X[:, 0], X[:, 1], s=30, c="gray", alpha=0.6)
axes[0].set_title("元データ(ラベルなし)")
axes[0].set_xlabel("特徴量1")
axes[0].set_ylabel("特徴量2")
# 右:k-means法によるクラスタリング結果
axes[1].scatter(X[:, 0], X[:, 1], c=y_pred,
cmap="viridis", s=30)
centers = kmeans.cluster_centers_
axes[1].scatter(centers[:, 0], centers[:, 1],
c="red", marker="X", s=200,
edgecolors="black", linewidths=1.5)
axes[1].set_title("k-means法によるクラスタリング結果")
axes[1].set_xlabel("特徴量1")
axes[1].set_ylabel("特徴量2")
plt.tight_layout()
plt.show()
入力 9.2: エルボー法によるクラスタ数の探索¶
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
# エルボー法:k=1〜10でイナーシャを計算
inertias = []
K_range = range(1, 11)
for k in K_range:
km = KMeans(n_clusters=k, random_state=0, n_init=10)
km.fit(X)
inertias.append(km.inertia_)
# イナーシャの推移をプロット
plt.figure(figsize=(5.8, 3.6))
plt.plot(K_range, inertias, marker="o")
plt.xlabel("クラスタ数 k")
plt.ylabel("イナーシャ")
plt.title("エルボー法によるクラスタ数の決定")
plt.xticks(K_range)
plt.grid(True)
plt.show()
入力 9.3: Irisデータセットの読み込み¶
import seaborn as sns
# Irisデータセットの読み込み(seaborn版)
df_iris = sns.load_dataset("iris")
feature_cols = ["sepal_length", "sepal_width",
"petal_length", "petal_width"]
X_iris = df_iris[feature_cols].values # 特徴量(4次元)
y_iris = df_iris["species"] # 真の品種ラベル(クラスタリングでは使わない)
print(f"データの形状: {X_iris.shape}")
print(f"品種名: {list(y_iris.unique())}")
入力 9.4: Irisデータへのk-means法の適用と比較¶
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
# k-means法を適用(ラベルは使わない)
kmeans_iris = KMeans(n_clusters=3, random_state=0, n_init=10)
kmeans_iris.fit(X_iris)
y_km = kmeans_iris.labels_
# クラスタリング結果と真のラベルを比較(花弁の長さと幅で可視化)
fig, axes = plt.subplots(1, 2, figsize=(5.8, 2.4))
sc0 = axes[0].scatter(X_iris[:, 2], X_iris[:, 3],
c=y_km, cmap="viridis", s=40)
axes[0].legend(*sc0.legend_elements(), title="クラスタ")
axes[0].set_title("k-means法によるクラスタリング結果")
axes[0].set_xlabel("花弁の長さ (cm)")
axes[0].set_ylabel("花弁の幅 (cm)")
sc1 = axes[1].scatter(X_iris[:, 2], X_iris[:, 3],
c=y_iris.astype("category").cat.codes,
cmap="viridis", s=40)
handles, _ = sc1.legend_elements()
axes[1].legend(handles, list(y_iris.unique()))
axes[1].set_title("真の品種ラベル")
axes[1].set_xlabel("花弁の長さ (cm)")
axes[1].set_ylabel("花弁の幅 (cm)")
plt.tight_layout()
plt.show()
入力 9.5: 凝集型クラスタリングの実行¶
from sklearn.cluster import AgglomerativeClustering
# 凝集型クラスタリング(k=3)
agg = AgglomerativeClustering(n_clusters=3, linkage="ward")
y_agg = agg.fit_predict(X)
入力 9.6: デンドログラムの描画¶
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage
# データ数が多いと見づらいため、50個のサンプルで実行
rng = np.random.default_rng(0)
indices = rng.choice(len(X), 50, replace=False)
X_sample = X[indices]
# 連結行列の計算
Z = linkage(X_sample, method="ward")
# デンドログラムの描画
plt.figure(figsize=(5.8, 2.4))
dendrogram(Z, leaf_rotation=90, leaf_font_size=8)
plt.title("デンドログラム(Ward法)")
plt.xlabel("サンプル番号")
plt.ylabel("距離")
plt.tight_layout()
plt.show()
入力 9.7: シルエットスコアの計算¶
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
# クラスタ数を変えてシルエットスコアを計算
scores = []
K_range = range(2, 11)
for k in K_range:
km = KMeans(n_clusters=k, random_state=0, n_init=10)
labels = km.fit_predict(X)
score = silhouette_score(X, labels)
scores.append(score)
print(f"k={k}: シルエットスコア = {score:.4f}")
入力 9.8: シルエットスコアの推移のプロット¶
import matplotlib.pyplot as plt
# シルエットスコアの推移をプロット
plt.figure(figsize=(5.8, 3.6))
plt.plot(K_range, scores, marker="o")
plt.xlabel("クラスタ数 k")
plt.ylabel("平均シルエットスコア")
plt.title("シルエットスコアによる最適クラスタ数の探索")
plt.xticks(K_range)
plt.grid(True)
plt.show()
入力 9.9: シルエットプロットの描画¶
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples
from sklearn.metrics import silhouette_score
# k=3でクラスタリング
km = KMeans(n_clusters=3, random_state=0, n_init=10)
y_km = km.fit_predict(X)
# 各データ点のシルエット係数を計算
silhouette_vals = silhouette_samples(X, y_km)
# シルエットプロットの描画
fig, ax = plt.subplots(figsize=(5.8, 4.4))
y_lower = 10
for i in range(3):
# クラスタiのシルエット係数を昇順にソート
cluster_silhouette_vals = silhouette_vals[y_km == i]
cluster_silhouette_vals.sort()
y_upper = y_lower + len(cluster_silhouette_vals)
ax.barh(range(y_lower, y_upper),
cluster_silhouette_vals,
height=1.0, edgecolor="none", alpha=0.7)
ax.text(-0.02,
y_lower + 0.5 * len(cluster_silhouette_vals),
f"クラスタ {i}", fontsize=12,
ha="right", va="center")
y_lower = y_upper + 10
# 平均シルエットスコアの線を描画
avg_score = silhouette_score(X, y_km)
ax.axvline(x=avg_score, color="red", linestyle="--",
label=f"平均: {avg_score:.3f}")
ax.set_xlim(-0.25, 0.85) # 左側にクラスタ名の余白を確保
ax.set_xlabel("シルエット係数")
ax.set_ylabel("クラスタ内のデータ点")
ax.set_title("シルエットプロット(k=3)")
ax.legend()
plt.tight_layout()
plt.show()
入力 9.10: PCAによる2次元への削減¶
from sklearn.decomposition import PCA
# Irisデータ(X_iris)は入力9.3で読み込み済み
# PCAで4次元→2次元に削減
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_iris)
print(f"元の次元数: {X_iris.shape[1]}")
print(f"削減後の次元数: {X_pca.shape[1]}")
入力 9.11: PCA後のデータの散布図による可視化¶
import matplotlib.pyplot as plt
# PCA後のデータを散布図で可視化
plt.figure(figsize=(5.8, 4.4))
for name in y_iris.unique():
mask = (y_iris == name).values
plt.scatter(X_pca[mask, 0],
X_pca[mask, 1],
label=name, s=40,
alpha=0.7)
plt.xlabel("第1主成分")
plt.ylabel("第2主成分")
plt.title("PCAによるIrisデータの2次元可視化")
plt.legend()
plt.grid(True)
plt.show()
入力 9.12: 各主成分の寄与率の確認¶
# 各主成分の寄与率
ratios = pca.explained_variance_ratio_
print("各主成分の寄与率:")
for i, ratio in enumerate(ratios):
print(f" 第{i+1}主成分: {ratio:.4f}"
f" ({ratio*100:.1f}%)")
total = ratios.sum()
print(f"累積寄与率: {total:.4f}"
f" ({total*100:.1f}%)")
入力 9.13: 寄与率と累積寄与率の可視化¶
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
# 4つの主成分すべての寄与率を確認
pca_full = PCA()
pca_full.fit(X_iris)
# 寄与率と累積寄与率のプロット
fig, ax = plt.subplots(figsize=(5.8, 3.6))
n_components = len(pca_full.explained_variance_ratio_)
x = range(1, n_components + 1)
ax.bar(x, pca_full.explained_variance_ratio_,
alpha=0.7, label="各主成分の寄与率")
ax.step(x, np.cumsum(pca_full.explained_variance_ratio_),
where="mid", color="red", label="累積寄与率")
ax.set_xlabel("主成分")
ax.set_ylabel("寄与率")
ax.set_title("各主成分の寄与率と累積寄与率")
ax.set_xticks(x)
ax.set_xticklabels([f"PC{i}" for i in x])
ax.legend()
ax.grid(True, axis="y")
plt.tight_layout()
plt.show()
入力 9.14: PCA後のデータへのk-means法の適用¶
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
# PCAで2次元に削減したデータにk-means法を適用
kmeans_pca = KMeans(n_clusters=3, random_state=0, n_init=10)
y_km_pca = kmeans_pca.fit_predict(X_pca)
# 可視化:k-means法の結果 vs 真のラベル
fig, axes = plt.subplots(1, 2, figsize=(5.8, 2.4))
axes[0].scatter(X_pca[:, 0], X_pca[:, 1],
c=y_km_pca, cmap="viridis", s=40)
axes[0].set_title("k-means法の結果(PCA後の2次元データ)")
axes[0].set_xlabel("第1主成分")
axes[0].set_ylabel("第2主成分")
axes[1].scatter(X_pca[:, 0], X_pca[:, 1],
c=y_iris.astype("category").cat.codes, cmap="viridis", s=40)
axes[1].set_title("真の品種ラベル")
axes[1].set_xlabel("第1主成分")
axes[1].set_ylabel("第2主成分")
plt.tight_layout()
plt.show()
入力 9.15: PCAとt-SNEの結果の比較¶
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
# 手書き数字データセット(64次元・10クラス)を読み込む
digits = load_digits()
X_digits, y_digits = digits.data, digits.target
# PCAとt-SNEでそれぞれ2次元に削減
X_pca_digits = PCA(n_components=2).fit_transform(X_digits)
tsne = TSNE(n_components=2, random_state=0, perplexity=30)
X_tsne_digits = tsne.fit_transform(X_digits)
# 色を数字の種類(0〜9)に対応させて描画
fig, axes = plt.subplots(1, 2, figsize=(5.8, 2.4))
axes[0].scatter(X_pca_digits[:, 0], X_pca_digits[:, 1],
c=y_digits, cmap="tab10", s=8)
axes[0].set_title("PCA")
axes[0].set_xlabel("第1主成分")
axes[0].set_ylabel("第2主成分")
axes[1].scatter(X_tsne_digits[:, 0], X_tsne_digits[:, 1],
c=y_digits, cmap="tab10", s=8)
axes[1].set_title("t-SNE")
axes[1].set_xlabel("成分1")
axes[1].set_ylabel("成分2")
plt.tight_layout()
plt.show()