Skip to content

第 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()