Preprocessing Data#

Import Library#

# Import semua library yang diperlukan
import pandas as pd
import numpy as np
from scipy.io import arff
import matplotlib.pyplot as plt
import seaborn as sns
from collections import Counter

# Preprocessing
from sklearn.preprocessing import StandardScaler
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split

# Set style
plt.style.use('default')
sns.set_palette("tab10")

Muat dan Siapkan Data#

train_path = 'ECG200/ECG200_TRAIN.arff'
test_path = 'ECG200/ECG200_TEST.arff'

data_train, _ = arff.loadarff(train_path)
data_test, _ = arff.loadarff(test_path)

df_train = pd.DataFrame(data_train)
df_test = pd.DataFrame(data_test)

X_train = df_train.iloc[:, :-1].values  # 96 kolom
y_train = df_train['target'].apply(lambda x: 1 if x == b'1' else -1).values

X_test = df_test.iloc[:, :-1].values
y_test = df_test['target'].apply(lambda x: 1 if x == b'1' else -1).values

print(f"Data latih: {X_train.shape}, Label: {y_train.shape}")
print(f"Data uji: {X_test.shape}, Label: {y_test.shape}")
Data latih: (100, 96), Label: (100,)
Data uji: (100, 96), Label: (100,)

Cek Distribusi Kelas Sebelum Balancing#

counter_before = Counter(y_train)
print("📉 Distribusi kelas SEBELUM balancing:")
print(f"  Kelas -1 (Abnormal): {counter_before[-1]}")
print(f"  Kelas  1 (Normal)  : {counter_before[1]}")

# Visualisasi sebelum
plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
bars = plt.bar(counter_before.keys(), counter_before.values(), color=['red', 'green'], alpha=0.7)
plt.title('Distribusi Kelas - Sebelum Balancing', fontsize=12)
plt.xlabel('Kelas')
plt.ylabel('Jumlah Sampel')
plt.xticks([-1, 1], ['Abnormal (-1)', 'Normal (1)'])
for bar in bars:
    plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5, 
             str(int(bar.get_height())), ha='center', va='bottom')

plt.tight_layout()
plt.show()
📉 Distribusi kelas SEBELUM balancing:
  Kelas -1 (Abnormal): 31
  Kelas  1 (Normal)  : 69
_images/ed3a3926f305e716e3801be90212042e3925300d40ec580635de6f09eb5cc73d.png

Normalisasi Data#

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

print("✅ Data telah dinormalisasi.")
✅ Data telah dinormalisasi.

Simpan Scaler#

import pandas as pd
import numpy as np
from scipy.io import arff
from sklearn.preprocessing import StandardScaler
import joblib

# Muat data latih mentah
train_path = 'ECG200/ECG200_TRAIN.arff'
data_train, _ = arff.loadarff(train_path)
df_train = pd.DataFrame(data_train)

X_train_raw = df_train.iloc[:, :-1].values

# Fit scaler
scaler = StandardScaler()
scaler.fit(X_train_raw)

# Simpan
joblib.dump(scaler, 'ECG200/scaler.pkl')
print("✅ Scaler berhasil disimpan!")
✅ Scaler berhasil disimpan!

Lakukan Balancing dengan SMOTE#

# Cek apakah data imbalance
if counter_before[-1] != counter_before[1]:
    print("🔧 Data tidak seimbang → lakukan balancing dengan SMOTE...")
    
    # SMOTE (aplikasikan hanya pada data latih!)
    smote = SMOTE(random_state=42)
    X_train_bal, y_train_bal = smote.fit_resample(X_train_scaled, y_train)
    
    print(f"✅ Setelah balancing: {X_train_bal.shape[0]} sampel")
else:
    print("✅ Data sudah seimbang. Lewati SMOTE.")
    X_train_bal, y_train_bal = X_train_scaled, y_train

# Simpan hasil balancing
counter_after = Counter(y_train_bal)
🔧 Data tidak seimbang → lakukan balancing dengan SMOTE...
✅ Setelah balancing: 138 sampel

Visualisasi Distribusi Setelah Balancing#

# Visualisasi sebelum & sesudah
fig, ax = plt.subplots(1, 2, figsize=(12, 5))

# Sebelum
ax[0].bar(counter_before.keys(), counter_before.values(), color=['red', 'green'], alpha=0.7)
ax[0].set_title('Sebelum Balancing', fontsize=12)
ax[0].set_xlabel('Kelas')
ax[0].set_ylabel('Jumlah Sampel')
ax[0].set_xticks([-1, 1])
ax[0].set_xticklabels(['Abnormal (-1)', 'Normal (1)'])
for k, v in counter_before.items():
    ax[0].text(k, v + 1, str(v), ha='center', va='bottom')

# Sesudah
ax[1].bar(counter_after.keys(), counter_after.values(), color=['red', 'green'], alpha=0.7)
ax[1].set_title('Setelah Balancing (SMOTE)', fontsize=12)
ax[1].set_xlabel('Kelas')
ax[1].set_ylabel('Jumlah Sampel')
ax[1].set_xticks([-1, 1])
ax[1].set_xticklabels(['Abnormal (-1)', 'Normal (1)'])
for k, v in counter_after.items():
    ax[1].text(k, v + 1, str(v), ha='center', va='bottom')

plt.suptitle('Distribusi Kelas: Sebelum vs Setelah Balancing', fontsize=14)
plt.tight_layout()
plt.show()
_images/c6741a767321abe4291328cafd7d5e4ad005eca43b43b4017740e3df19ba60bf.png

Simpan Data Siap Model#

# Simpan ke file .npy
np.save('ECG200/X_train_bal.npy', X_train_final)
np.save('ECG200/y_train_bal.npy', y_train_final)
np.save('ECG200/X_test_bal.npy', X_test_final)
np.save('ECG200/y_test_bal.npy', y_test_final)

print("✅ Data berhasil disimpan sebagai .npy di folder ECG200/")
✅ Data berhasil disimpan sebagai .npy di folder ECG200/