이번 프로젝트는 kaggle에 있는 'loan_default' 데이터를 가지고
데이터 전처리와 로지스틱 모형을 적용해 보는 프로젝트였다.
오늘 하루 종일 class 가지고 연습을 많이 해 볼 수 있었다.
(GPT에게 도움을 구해도 자꾸 엉뚱한 걸 알려주더라. 내가 원하는 것을
명확하게 말하지 못한 탓이겠지.)
class DataPreprocessor:
def __init__(self,target_var,delete_var=None, test_rate=0.2, random_state=42,
missing_value_rate=0.01, skewness_abs_criterion=2, log_transform_cols=None,scaling_method=None):
# class 정의할 때 test_rate=0.2 처럼 초기값을 입력해줘야함
# 외부에서 정한 값을 직접 참조하는 구조가 아님
self.target_var = target_var
self.delete_var = delete_var if delete_var else []
self.test_rate = test_rate
self.random_state = random_state
self.missing_value_rate = missing_value_rate
self.skewness_abs_criterion = skewness_abs_criterion
self.log_transform_cols = log_transform_cols if log_transform_cols is not None else []
self.scaling_method = scaling_method.lower() if scaling_method else None
def fit_transform(self, df):
# fit_transform이라는 매서드 정의
df = df.copy() # pandas warning방지 차원에서 .copy()
# 1. Drop delete_vars, 불필요한 컬럼 제거
df.drop(columns = self.delete_var, inplace = True, errors = 'ignore')
# 2. Separate target
y = df[self.target_var] # 수정됨
df.drop(columns=[self.target_var], inplace=True) # 수정됨
# 3. Column type 나누기(수치형, 범주형)
num_cols = df.select_dtypes(include = ['float64','int64']).columns
cat_cols = df.select_dtypes(include = ['object']).columns
# 4. log1p 대상 자동 판단 (없으면)
if not self.log_transform_cols:
skewed = df[num_cols].skew().abs()
self.log_transform_cols = skewed[skewed > self.skewness_abs_criterion].index.tolist()
# 5. 결측 비율 기준 행 제거
missing_ratio = df.isnull().mean()
cols_to_dropna = missing_ratio[missing_ratio < self.missing_value_rate].index.tolist()
df.dropna(subset=cols_to_dropna, inplace=True) # 수정됨
y = y.loc[df.index] # 수정됨: row 삭제 후 target 재정렬
# 6. 남은 결측치 처리
for col in df.columns:
if col in num_cols:
if col in self.log_transform_cols:
df[col] = df[col].clip(lower=0) # 수정됨: 음수 방지
df[col] = np.log1p(df[col])
df[col] = df[col].fillna(df[col].median())
elif col in cat_cols:
df[col] = df[col].fillna("Missing_value")
# 7. 범주형 변수 One-Hot 인코딩
df_cat = pd.get_dummies(df[cat_cols], drop_first=True)
df_num = df[num_cols]
# 8. Scaling 선택적
if self.scaling_method:
if self.scaling_method=='standard':
scaler = StandardScaler()
elif self.scaling_method=='min_max':
scaler = MinMaxScaler()
elif self.scaling_method=='robust':
scaler = RobustScaler()
else:
raise ValueError("Unsupported scaling_method. Choose from 'standard', 'min_max', 'robust', or None.")
# 9. 피처 조합
X = pd.concat([df_num, df_cat], axis=1) # y는 앞에서 정의함
# 🔧 컬럼 이름 정제(xg-boost feature문제 때)
X.columns = clean_feature_names(X.columns)
# 10. train-test split
return train_test_split(X, y, test_size=self.test_rate,
random_state=self.random_state, stratify=y)
prep = DataPreprocessor(
target_var='Status',
delete_var=['ID', 'year'],
test_rate=0.2,
random_state=42,
missing_value_rate=0.01,
skewness_abs_criterion=2
)
X_train, X_test, y_train, y_test = prep.fit_transform(Loan_Default_df)
from sklearn.linear_model import LogisticRegression # 로지스틱 모형 불러오기
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 모델 평가지표(accuracy, precision, recall, f1_score)
import time
st = time.time()
num_iter= 3000 # 1000번 하니 warning나옴
lr_clf = LogisticRegression(max_iter = num_iter, n_jobs=-1)
lr_clf.fit(X_train, y_train)
lr_pred = lr_clf.predict(X_test)
ed = time.time()
print(f'소모시간 : {round(ed-st,2)}초')
print(f'number_of_iteration : {num_iter}')
print('\n')
print("Accuracy:", round(accuracy_score(y_test, lr_pred),4))
print("Precision:", round(precision_score(y_test, lr_pred),4))
print("Recall:", round(recall_score(y_test, lr_pred),4))
print("F1 Score:", round(f1_score(y_test, lr_pred),4))
로지스틱 모형 성능(변수 5개 제거 후)
변수 제거 사유
모델 | 소요시간(초) | Accuracy | Precision | Recall | F1-score |
---|---|---|---|---|---|
Random Forest | 17.02 | 0.934 | 0.8917 | 0.8321 | 0.8609 |
XGBoost | 8.57 | 0.9361 | 0.8736 | 0.8648 | 0.8692 |
LightGBM | 2.07 | 0.9373 | 0.8706 | 0.8745 | 0.8725 |
Logistic Regression | 77.91 | 0.8713 | 0.9188 | 0.5212 | 0.6651 |