Banyak dataset medis memiliki puluhan fitur numerik. Sebagian fitur membawa pola penting, sebagian lain bisa membuat model lebih sulit dibaca. Di studi kasus ini, kita akan melihat bagaimana PCA dan LDA dapat digunakan untuk menyederhanakan representasi data sebelum klasifikasi.
Materi ini hanya untuk edukasi machine learning, bukan alat diagnosis medis.
Dari paper ke workflow praktik.
Paper sumber membahas reduksi dimensi untuk membantu deteksi kanker payudara pada data terstruktur. Di tutorial ini, kita membangun ulang workflow yang mudah dijalankan: baseline model, transformasi PCA, transformasi LDA, lalu perbandingan metrik.
Masalah machine learning-nya adalah binary classification. Input berupa fitur numerik dari dataset breast cancer, output berupa kelas diagnosis atau label target. PCA tidak memakai label saat mencari komponen utama, sedangkan LDA memakai label untuk mencari proyeksi pemisah kelas.
Alur eksperimen dari data sampai insight.
- Load dataset breast cancer dari URL publik
- Membersihkan kolom ID atau unnamed
- Mendeteksi target secara fleksibel
- Scaling fitur numerik
- Baseline Logistic Regression
- PCA dua komponen dan visualisasi 2D
- LDA satu komponen dan visualisasi distribusi kelas
- Membandingkan baseline, PCA, dan LDA dengan metrik evaluasi
Ubah masalah nyata menjadi task machine learning.
Tujuannya bukan membuat alat diagnosis, tetapi memahami kapan reduksi dimensi membantu workflow klasifikasi. Kita akan melihat data fitur asli, data setelah PCA, dan data setelah LDA sebagai tiga jalur eksperimen yang dibandingkan secara transparan.
Setup Environment
Kita tetap memakai dependency ringan: pandas, numpy, matplotlib, dan scikit-learn.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.metrics import (
accuracy_score,
precision_score,
recall_score,
f1_score,
classification_report,
confusion_matrix
)
import warnings
warnings.filterwarnings("ignore")
Environment siap untuk data tabular, transformasi dimensi, training model, dan evaluasi.
Load Dataset
Dataset dibaca langsung dari URL publik. Jika kamu memakai file lokal, cukup ganti DATA_URL menjadi path CSV di komputermu.
import pandas as pd
DATA_URL = "https://autotrain.app/opendata92bd.html?file=breast-cancer.csv"
df = pd.read_csv(DATA_URL)
df.head()
Output awal biasanya berisi banyak fitur numerik dan satu kolom target seperti diagnosis, target, class, label, atau kolom terakhir.
Data Understanding dan Target Detection
Karena dataset dari sumber berbeda bisa memakai nama target yang berbeda, kode ini mencari target secara fleksibel.
print("Shape:", df.shape)
df.info()
df.isnull().sum().sort_values(ascending=False).head(20)
possible_targets = ["diagnosis", "target", "class", "label"]
target_col = None
for col in possible_targets:
if col in df.columns:
target_col = col
break
if target_col is None:
target_col = df.columns[-1]
print("Target column:", target_col)
df[target_col].value_counts()
Output Target column memastikan kita tidak salah memilih label. Jika fallback ke kolom terakhir, cek lagi secara manual sebelum lanjut.
Bagian praktik lengkap: preprocessing, training, evaluasi, interpretasi, dan file download.
Ringkasan, problem framing, dataset overview, dan preview kode awal tetap bisa dibaca gratis. Untuk mengikuti workflow penuh seperti notebook praktik, gunakan Project Access.
Clean Unnecessary Columns
Kolom ID atau unnamed biasanya tidak membawa sinyal prediktif. Kita hapus agar model fokus pada fitur numerik yang relevan.
df_clean = df.copy()
for col in df_clean.columns:
if "unnamed" in col.lower() or col.lower() == "id":
df_clean = df_clean.drop(columns=[col])
df_clean.head()
Dataset menjadi lebih bersih. Jika ada kolom non-fitur lain, kamu dapat menambahkannya ke aturan drop.
Split X dan y
PCA dan LDA bekerja pada data numerik, jadi X dibuat hanya dari kolom integer dan float.
X = df_clean.drop(columns=[target_col])
y = df_clean[target_col]
X = X.select_dtypes(include=["int64", "float64"])
print("X shape:", X.shape)
print("y shape:", y.shape)
target_encoder = LabelEncoder()
y_encoded = target_encoder.fit_transform(y.astype(str))
print("Target classes:")
for idx, label in enumerate(target_encoder.classes_):
print(idx, "->", label)
Target classes membantu membaca kembali label asli setelah model mengeluarkan angka prediksi.
Train Test Split dan Scaling
Scaling wajib terasa di sini karena PCA, LDA, dan Logistic Regression sensitif terhadap skala fitur.
X_train, X_test, y_train, y_test = train_test_split(
X,
y_encoded,
test_size=0.2,
random_state=42,
stratify=y_encoded
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
Scaler hanya fit pada data training untuk menghindari data leakage dari test set.
Baseline Model tanpa PCA/LDA
Baseline dibutuhkan supaya kita tahu apakah reduksi dimensi benar-benar membantu atau justru mengurangi informasi penting.
from sklearn.linear_model import LogisticRegression
baseline_model = LogisticRegression(max_iter=1000, random_state=42)
baseline_model.fit(X_train_scaled, y_train)
baseline_preds = baseline_model.predict(X_test_scaled)
baseline_scores = {
"model": "Baseline Logistic Regression",
"accuracy": accuracy_score(y_test, baseline_preds),
"precision": precision_score(y_test, baseline_preds, zero_division=0),
"recall": recall_score(y_test, baseline_preds, zero_division=0),
"f1": f1_score(y_test, baseline_preds, zero_division=0)
}
baseline_scores
baseline_scores menjadi pembanding utama untuk PCA dan LDA.
PCA Transformation
PCA mencari dua arah variasi terbesar. Karena tidak melihat label, PCA cocok untuk eksplorasi struktur data.
from sklearn.decomposition import PCA
pca = PCA(n_components=2, random_state=42)
X_train_pca = pca.fit_transform(X_train_scaled)
X_test_pca = pca.transform(X_test_scaled)
print("Explained variance ratio:", pca.explained_variance_ratio_)
print("Total explained variance:", pca.explained_variance_ratio_.sum())
plt.figure(figsize=(8, 6))
plt.scatter(
X_train_pca[:, 0],
X_train_pca[:, 1],
c=y_train,
alpha=0.7
)
plt.title("PCA Projection - Training Data")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.tight_layout()
plt.show()
Explained variance menunjukkan seberapa banyak informasi variasi data yang ditangkap dua komponen utama.
Classification After PCA
Setelah data diproyeksikan menjadi dua komponen, kita latih Logistic Regression pada ruang PCA.
pca_model = LogisticRegression(max_iter=1000, random_state=42)
pca_model.fit(X_train_pca, y_train)
pca_preds = pca_model.predict(X_test_pca)
pca_scores = {
"model": "Logistic Regression + PCA",
"accuracy": accuracy_score(y_test, pca_preds),
"precision": precision_score(y_test, pca_preds, zero_division=0),
"recall": recall_score(y_test, pca_preds, zero_division=0),
"f1": f1_score(y_test, pca_preds, zero_division=0)
}
pca_scores
Jika skor turun sedikit tetapi visualisasi jauh lebih mudah dibaca, PCA tetap bisa berguna untuk eksplorasi.
LDA Transformation
LDA memakai label untuk mencari proyeksi yang memisahkan kelas. Untuk binary classification, LDA menghasilkan satu komponen.
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda = LinearDiscriminantAnalysis(n_components=1)
X_train_lda = lda.fit_transform(X_train_scaled, y_train)
X_test_lda = lda.transform(X_test_scaled)
print("LDA train shape:", X_train_lda.shape)
plt.figure(figsize=(8, 4))
for class_id in np.unique(y_train):
plt.hist(
X_train_lda[y_train == class_id, 0],
bins=20,
alpha=0.6,
label=str(target_encoder.inverse_transform([class_id])[0])
)
plt.title("LDA Projection - Training Data")
plt.xlabel("Linear Discriminant 1")
plt.ylabel("Frequency")
plt.legend()
plt.tight_layout()
plt.show()
Jika histogram antar kelas semakin terpisah, LDA menemukan proyeksi yang informatif untuk klasifikasi.
Classification After LDA
Kita pakai Logistic Regression lagi agar perbandingan baseline, PCA, dan LDA tetap adil.
lda_model = LogisticRegression(max_iter=1000, random_state=42)
lda_model.fit(X_train_lda, y_train)
lda_preds = lda_model.predict(X_test_lda)
lda_scores = {
"model": "Logistic Regression + LDA",
"accuracy": accuracy_score(y_test, lda_preds),
"precision": precision_score(y_test, lda_preds, zero_division=0),
"recall": recall_score(y_test, lda_preds, zero_division=0),
"f1": f1_score(y_test, lda_preds, zero_division=0)
}
lda_scores
Karena LDA supervised, performanya bisa sangat baik jika kelas memang terpisah pada kombinasi linear fitur.
Compare Results
Sekarang semua jalur eksperimen dibaca di satu tabel dan satu plot.
comparison_df = pd.DataFrame([
baseline_scores,
pca_scores,
lda_scores
]).sort_values(by="f1", ascending=False)
comparison_df
comparison_df.set_index("model")[["accuracy", "precision", "recall", "f1"]].plot(
kind="bar",
figsize=(10, 5)
)
plt.title("Model Performance Comparison")
plt.ylabel("Score")
plt.ylim(0, 1)
plt.xticks(rotation=30, ha="right")
plt.tight_layout()
plt.show()
Bandingkan metrik secara utuh. Pada konteks medis, recall sering perlu dibaca serius, tetapi keputusan metrik tetap bergantung tujuan eksperimen.
Confusion Matrix Model Terbaik
Confusion matrix memperjelas jumlah prediksi benar dan salah pada tiap kelas.
best_row = comparison_df.iloc[0]
best_model_name = best_row["model"]
if best_model_name == "Baseline Logistic Regression":
best_preds = baseline_preds
elif best_model_name == "Logistic Regression + PCA":
best_preds = pca_preds
else:
best_preds = lda_preds
cm = confusion_matrix(y_test, best_preds)
fig, ax = plt.subplots(figsize=(6, 5))
ax.imshow(cm)
ax.set_title(f"Confusion Matrix - {best_model_name}")
ax.set_xlabel("Predicted Label")
ax.set_ylabel("True Label")
ax.set_xticks(range(len(target_encoder.classes_)))
ax.set_yticks(range(len(target_encoder.classes_)))
ax.set_xticklabels(target_encoder.classes_)
ax.set_yticklabels(target_encoder.classes_)
for i in range(cm.shape[0]):
for j in range(cm.shape[1]):
ax.text(j, i, cm[i, j], ha="center", va="center")
plt.tight_layout()
plt.show()
Diagonal utama adalah prediksi benar. Kesalahan di luar diagonal harus dilihat dengan hati-hati karena konteks dataset medis.
Insight akhir dan batasan eksperimen.
- PCA tidak melihat label dan fokus mencari variasi terbesar, sehingga bagus untuk eksplorasi struktur data.
- LDA memakai label dan fokus mencari proyeksi yang memisahkan kelas, sehingga sering menarik untuk klasifikasi.
- Performa dapat berubah karena dataset, preprocessing, model, dan train-test split.
- Dalam konteks medis, model ML tidak boleh dianggap sebagai diagnosis final. Tujuan tutorial ini adalah memahami workflow eksperimen.
File pendukung studi kasus.
Gunakan notebook untuk belajar bertahap, atau pakai source code jika ingin langsung menjalankan eksperimen di environment sendiri.
