import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import (
    accuracy_score,
    confusion_matrix, ConfusionMatrixDisplay
)
from sklearn.decomposition import PCA


# ]ʂ̕\
def eval_and_print(name, y_true, y_pred, target_names=None):
    print(f"\n[{name}]")
    print("Accuracy :", accuracy_score(y_true, y_pred))

    # s
    cm = confusion_matrix(y_true, y_pred)
    ConfusionMatrixDisplay(
        confusion_matrix=cm, display_labels=target_names
    ).plot(cmap="Greys")
    plt.title(f"Confusion Matrix - {name}")
    plt.show()


# 1) f[^ǂݍ݂ƕ
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)


# x[XCiŕpNX\j
majority_class = pd.Series(y_train).mode()[0]
y_pred_base = [majority_class] * len(y_test)
print("[Baseline] Most frequent class =",
    iris.target_names[majority_class])
print("[Baseline] Accuracy:", accuracy_score(y_test, y_pred_base), "\n")


# 2) pCvCiW  k-NNj
pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("knn", KNeighborsClassifier())
])


# 3) ObhT[`i5-fold CVj
param_grid = {
    "knn__n_neighbors": [1, 3, 5, 7, 9, 11],
    "knn__weights": ["uniform", "distance"],
    "knn__p": [1, 2],  # 1:}nb^, 2:[Nbh
}
gs = GridSearchCV(pipe, param_grid, cv=5, n_jobs=-1)
gs.fit(X_train, y_train)


print("Best CV score:", gs.best_score_)
print("Best params:", gs.best_params_)


# 4) k-NNŗ\ƕ]
best_model = gs.best_estimator_
y_pred = best_model.predict(X_test)
eval_and_print("k-NN", y_test, y_pred, iris.target_names)


# 5) FPCA 2ɓeāA\Ɛʐ}ŕ\
pca = PCA(n_components=2, random_state=42)
X_pca = pca.fit_transform(X)


# \xiSf[^ɑ΂ĊwKς݃fŗ\j
pipe.fit(X, y)
y_all_pred = best_model.predict(X)


markers = ["o", "s", "^"]
plt.figure()
plt.title("Iris (Predicted Labels by k-NN)")
for cls in np.unique(y_all_pred):
    plt.scatter(
        X_pca[y_all_pred == cls, 0],
        X_pca[y_all_pred == cls, 1],
        marker=markers[cls % len(markers)],
        edgecolor="k",
        linewidths=0.2,
        label=iris.target_names[cls]
    )
plt.legend()
plt.xlabel("PC1"); plt.ylabel("PC2")
plt.tight_layout()
plt.show()


# QlF^̃x
plt.figure()
plt.title("Iris (Ground-Truth Labels)")
for cls in np.unique(y):
    plt.scatter(
        X_pca[y == cls, 0],
        X_pca[y == cls, 1],
        marker=markers[cls % len(markers)],
        edgecolor="k",
        linewidths=0.2,
        label=iris.target_names[cls]
    )
plt.legend()
plt.xlabel("PC1"); plt.ylabel("PC2")
plt.tight_layout()
plt.show()