================================================================================
  Question 56: K-Means Clustering on Iris Dataset with Elbow Method & PCA Visualization
================================================================================

FULL QUESTION:
Perform unsupervised K-Means clustering on the Iris dataset without using species target labels:
1. Standardize features to zero mean and unit variance using StandardScaler.
2. Determine the optimal number of clusters (k) using the Elbow Method (Within-Cluster Sum of Squares / Inertia)
   and calculate Silhouette Scores across k = 2 to 8.
3. Train the K-Means algorithm with optimal k=3.
4. Apply Principal Component Analysis (PCA) to reduce 4D features to 2D for visual inspection.
5. Plot the discovered clusters and cluster centroids on the 2D PCA projection.

SHORT QUESTION:
Unsupervised K-Means clustering on Iris dataset with Elbow curve, Silhouette analysis, and 2D PCA scatter plot.

================================================================================
README & SETUP INSTRUCTIONS:
================================================================================
# Question 56: K-Means Clustering on Iris Dataset

## Requirements
- Python 3.x
- Libraries: pandas, numpy, scikit-learn, matplotlib

## How to Run
```bash
python question_56_kmeans_iris.py
```

================================================================================
SOURCE CODE FILES:
================================================================================

1. question_56_kmeans_iris.py
--------------------------------------------------------------------------------
# ============================================================
# K-MEANS CLUSTERING ON IRIS DATASET
# Elbow Method, Silhouette Score, and PCA 2D Visualization
# ============================================================

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler

# ------------------------------------------------------------
# 1. Load Dataset
# ------------------------------------------------------------
iris = load_iris()
X = iris.data  # Four numerical features
y_true = iris.target  # Ground truth species labels

print("Dataset Shape:", X.shape)
print("Features:", iris.feature_names)

# ------------------------------------------------------------
# 2. Standardize Features
# ------------------------------------------------------------
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# ------------------------------------------------------------
# 3. Elbow Method
# ------------------------------------------------------------
inertia = []
K_range = range(1, 11)

for k in K_range:
    kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
    kmeans.fit(X_scaled)
    inertia.append(kmeans.inertia_)

df_inertia = pd.DataFrame({"K": list(K_range), "Inertia": inertia})
print("\nInertia values across K:")
print(df_inertia.to_string(index=False))

# Plot Elbow Curve
plt.figure(figsize=(7, 5))
plt.plot(K_range, inertia, marker="o")
plt.title("Elbow Method for Optimal K")
plt.xlabel("Number of Clusters (K)")
plt.ylabel("Inertia")
plt.xticks(list(K_range))
plt.grid(True)
plt.tight_layout()
plt.show()

# ------------------------------------------------------------
# 4. Fit K-Means with Optimal K (K=3)
# ------------------------------------------------------------
optimal_k = 3
kmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
y_kmeans = kmeans.fit_predict(X_scaled)

# ------------------------------------------------------------
# 5. Clustering Evaluation & Centroids
# ------------------------------------------------------------
summary = pd.DataFrame({"Cluster": y_kmeans, "True_Label": y_true})

print("\nCluster Centroids (Standardized Feature Space):")
print(pd.DataFrame(kmeans.cluster_centers_, columns=iris.feature_names))

print("\nCluster Sample Counts:")
print(summary["Cluster"].value_counts().sort_index())

print("\nCrosstab (Cluster vs True Species Label):")
print(pd.crosstab(summary["Cluster"], summary["True_Label"]))

score = silhouette_score(X_scaled, y_kmeans)
print(f"\nSilhouette Score: {score:.4f}")

# Direct 2D feature scatter (Sepal Length vs Sepal Width)
plt.figure(figsize=(8, 6))
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=y_kmeans, cmap="viridis", s=50)
centers = kmeans.cluster_centers_
plt.scatter(
    centers[:, 0],
    centers[:, 1],
    c="red",
    s=200,
    alpha=0.75,
    marker="X",
    label="Centroids",
)
plt.title("K-Means Clustering on Iris (First 2 Features)")
plt.xlabel("Sepal Length (Standardized)")
plt.ylabel("Sepal Width (Standardized)")
plt.legend()
plt.tight_layout()
plt.show()

# ------------------------------------------------------------
# 6. PCA Projection for 2D Visualization
# ------------------------------------------------------------
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

print("\nExplained Variance Ratio by Component:")
print(pca.explained_variance_ratio_)
print(f"Total Variance Explained: {pca.explained_variance_ratio_.sum():.4f}")

centers_pca = pca.transform(kmeans.cluster_centers_)

plt.figure(figsize=(8, 6))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y_kmeans, cmap="viridis", s=50)
plt.scatter(
    centers_pca[:, 0],
    centers_pca[:, 1],
    c="red",
    s=200,
    alpha=0.75,
    marker="X",
    label="Centroids",
)
plt.title("K-Means Clustering on Iris Dataset using PCA")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.legend()
plt.tight_layout()
plt.show()
--------------------------------------------------------------------------------
