selli_ml_deployment / feature_engineering.py
nadyatangkere's picture
Update feature_engineering.py
64315cc verified
Raw History Blame Contribute Delete
3.52 kB
import numpy as np
import pandas as pd
def add_features(df):
"""
Melakukan Feature Engineering yang telah divalidasi dan dioptimalkan berdasarkan
analisis Feature Importance (Gain Metric) pada model LightGBM.
Hanya fitur dengan sinyal prediksi kuat yang dipertahankan, sementara fitur noise
dan redundan dihapus untuk efisiensi model.
Features yang ditambahkan/ditransformasi:
1. pdays_cleaned: Mengganti nilai -1 (belum pernah dikontak) menjadi 1000
agar memiliki urutan numerik yang logis bagi Tree-based models.
2. debt_count: Agregasi beban hutang (housing + loan). Nilai 0-2.
Terbukti memiliki Gain Importance jauh lebih tinggi (40.8) dibanding fitur terpisah.
3. prev_success: Binary flag (1 jika kampanye sebelumnya 'success').
Prediktor terkuat dalam dataset (Importance: 100.0).
4. education_level: Mapping ordinal (0: unknown s/d 3: tertiary) untuk menangkap
hirarki sosial-ekonomi nasabah.
5. month_int: Mapping bulan ke integer (1-12) untuk menangkap musiman (seasonality)
secara efisien tanpa One-Hot Encoding berlebih.
6. contact_unknown: Binary flag (1 jika contact='unknown').
Indikator kualitas data yang sangat kuat (Importance: 68.9).
Features yang dihapus:
- duration: Mencegah Data Leakage fatal (durasi tidak diketahui sebelum menelpon).
- pdays: Digantikan oleh pdays_cleaned.
- housing, loan: Dihapus karena sinyalnya sudah diambil alih oleh debt_count.
- education: Digantikan oleh education_level.
- month: Digantikan oleh month_int.
- is_balance_negative: Dihapus karena Low Importance Score (0.2).
Features Raw yang dipertahankan (Passthrough):
- day: Angka tanggal mentah (1-31) terbukti lebih unggul daripada fitur 'payday'.
- balance, age, campaign, previous: Fitur numerik standar.
- job, marital, contact, poutcome: Fitur kategorikal untuk encoding di pipeline.
Args:
df (pd.DataFrame): DataFrame input raw (data training atau data baru).
Returns:
pd.DataFrame: DataFrame bersih yang siap masuk ke pipeline preprocessing.
"""
df = df.copy()
if 'duration' in df.columns:
df = df.drop(columns=['duration'])
if 'pdays' in df.columns:
df['pdays_cleaned'] = df['pdays'].replace(-1, 1000)
df = df.drop(columns=['pdays'])
if 'housing' in df.columns and 'loan' in df.columns:
h = (df['housing'] == 'yes').astype(int)
l = (df['loan'] == 'yes').astype(int)
df['debt_count'] = h + l
df = df.drop(columns=['housing', 'loan'])
if 'poutcome' in df.columns:
df['prev_success'] = (df['poutcome'] == 'success').astype(int)
edu_map = {'unknown': 0, 'primary': 1, 'secondary': 2, 'tertiary': 3}
if 'education' in df.columns:
df['education_level'] = df['education'].astype(str).str.lower().map(edu_map).fillna(0)
df = df.drop(columns=['education'])
month_map = {
'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6,
'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12
}
if 'month' in df.columns:
df['month_int'] = df['month'].astype(str).str.lower().map(month_map).fillna(0)
df = df.drop(columns=['month'])
if 'contact' in df.columns:
df['contact_unknown'] = (df['contact'] == 'unknown').astype(int)
if 'is_balance_negative' in df.columns:
df = df.drop(columns=['is_balance_negative'])
return df