목표
- 데이터 전처리의 필요성과 핵심 방법
- 불균형 데이터 문제 해결
- 범주형 데이터 인코딩 기법
- 피처 엔지니어링 기법
- 실습
1. 데이터 전처리
- 원시(raw) 데이터에서 불필요하거나 손실(noise)이 있는 부분을 처리하고 분석 목적에 맞는 형태로 만드는 과정
필요성
- 모델 정확도 및 신뢰도 향상
- 이상치나 결측치가 많은 상태로 학습하면 예측 성능이 떨어짐
- 효율적인 데이터 분석과 모델 훈련을 위해 필수
제조업 사례
- 센서가 간헐적으로 측정에 실패(결측값)
- 센서 오작동으로 인해 극단적으로 큰 값이 기록(이상치)
- 정상 제품과 불량 제품의 데이터 분포가 매우 다름(불균형)
금융 사례
- 증권사나 은행에서 고객 정보가 유실되거나 특정 시점의 주가나 거래량 데이터가 취합되지 않은 경우(결측값)
- 특정 종목에 대해 드물게 발생하는 급등라그 단일 대량 거래에 따른 비정상적인 가격 변동(이상치)
- 카드사 사기(부정 거래) 데이터나 대출 연체 사례 데이터의 경우 정상 데이터에 비해 극소수(불균형)
마케팅 사례
- 온라인 설문이나 쿠폰 사용 정보가 중간에 누락되거나 특정 채널에서 전송된 쿠폰 수가 확인 불가한 경우(결측값)
- 광고 클릭률 중 특정 상품의 노출수/클릭수가 월등히 높아 평균을 왜곡 혹은 이벤트 끝나마자자 조회수 급감하는 경우(이상치)
- 고객 이탈 예측 시 '이탈 고객' 비율이 매우 적은 경우, 특정 광고 캠페인 전환 성공/실패 비율의 극단적 차이 발생하는 경우(불균형)
2. 결측치 처리
결측치 발생 원인
- 센서 고장, 측정 오류, 환경적 문제 등
- 사람이 수기로 입력하는 경우 누락 가능성 존재
처리 기법
- 삭제: 결측치가 있는 행 또는 열 제거
- 간단하지만 데이터 손실이 발생
- 결측치가 소수일때 적합
- 대체: 다른 값을 채워 넣음
- 평균 또는 중앙값으로 대체: 수치형 데이터에서 많이 사용
- 최빈값으로 대체: 범주형 데이터에서 사용
- 예측 모델로 대체: 회귀/분류 모델을 이용해 결측값 예측
제조업 예시
- 센서 데이터에서 특정 시간대에 종종 측정 누락이 발생
- 중앙값으로 간단히 대체
- 인접 시간대 센서 값의 추세(이동평균)를 바탕으로 결측치 채움
- 중요한 센서라면 예측 모델 통해 결측치 추정 (다른 분야에도 적용 가능)
금융 예시
- 시간에 따라 변하는 시계열 데이터의 경우(주가, 거래량 등)
- 일정 기간의 주가의 평균 또는 가중평균을 통해 결측값 보정
- 직전, 직후에 기록된 유효한 데이터로 빈 값 채우는 방식(Forward/Backward Fill)
- 고객 데이터(신용도, 소득, 자산 등)에 결측치가 발생한 경우
- 중앙값을 사용하여 대체, 극단값의 영향을 받지 않아 평균 대체보다 안정적
- 그룹의 대표값으로 대체(결측값 속한 클러스터의 평균, 중앙값 등을 사용)
- 결측값이 간헐적으로 존재
- 비교적 중요도가 낮다면 제거
마케팅 예시
- 캠페인 반응(클릭률, 전환율) 데이터의 경우
- 온라인 광고나 캠페인 전환율이 일시적으로 누락된 경우, 인접 기간(전일, 전주 동일 요일) 데이터 기반으로 보정
- 시점별로 전환 흐름이 비교적 일정하거나 특정 패턴이 있다면, 이동평균으로 추정 가능
- 고객 설문/프로필 정보의 경우
- 나이, 지역, 성별 등의 간단한 정보는 최빈값/중앙값으로 대체 가능
- 결측값이 속한 클러스터의 평균, 중앙값으로 대체
- 정기적으로 누락되는 채널/시점 데이터의 경우
- 특정 채널 또는 시간대에 반복적으로 누락된다면 시스템(로그 수집, 쿠폰 트래킹 등)을 개선해 재발 방지
- 결측이 반복되는 구간은 유사 채널 지표로 추정
코드
- df.mode(): 각 열마다 최빈값을 탐색
- 중복이 있는 경우에는 어떤 것을 쓸지 판단해야 함
# 행 제거
df.dropna()
# 열 제거
df.dropna(axis=1)
# 평균값 대체
df.fillna(df.mean(numeric_only=True))
# 중앙값 대체
df.fillna(df.median(numeric_only=True))
# 최빈값 대체
max_value = df.mode().iloc[0]
df = df.fillna(max_value)
3. 이상치 탐지 및 제거
이상치
- 정상 범주에서 크게 벗어나는 값
- 장비 오작동, 환경정 특이 사항 등 원인이 다양
탐지 기법
- 통계적 기법 (3-시그마 Rule)
- 데이터가 정규분포 따른다고 가정하고 평균에서 ± 표준편차 범위 벗어나는 값 이상치로 간주
- 직관적이고 간단하나 정규성 가정이 틀릴 수 있음
- 박스플롯 기준
- 사분위수(IQR = Q3 - Q1)를 이용해 Q1 - 1.5 * IQR, Q3 + 1.5 * IQR 를 벗어나는 데이터를 이상치로 간주
- 분포 특성에 영향을 적게 받는 것이 장점
- 머신러닝 기반
- 이상치 탐지 알고리즘 (Isolation Forest, DBSCAN 등)
- 복합적 패턴을 고려
처리 기법
- 이상치 단순 제거(필요에 따라)
- 이상치 값을 조정(클리핑, Winsorizing 등)
- 특정 값을 넘는 값을 다 하나로 통일
- 별도로 구분하여 모델에서 제외하거나 다른 모델(이상치 예측 모델)로 활용
제조업 예시
- 특정 센서값이 정상 범위(20~80)에서 갑자기 200 기록 ➡️ 장비 이상 가능성 높음
- 매 생산 라인마다 센서값 범위가 조금씩 다를 수 있으므로 라인별 통계 기준(평균, 표준편차)으로 이상치 판단
금융 예시
- 특정 종목의 하루 거래량이 평소 대비 수십 배로 급등하거나 주가나 순간적으로 비정상적인 폭락을 보일 때 이상치 판단
- 고객 신용도 데이터에서도 소득이나 부채가 일반 범위에서 크게 벗어난 값ㄷ이 나오면 의심 거래나 데이터 오류 판단
마케팅 예시
- 어떤 광고 캠페인에서 클릭률(CTR)이 다른 캠페인에 비해 극단적으로 높아 평균 분석을 왜곡하는 경우 이상치 의심
- 고객 구매 이력에서 특정 기간에 평소와 전혀 다른 과도한 소비가 포착되면 마케팅 분석에서 이상치 분류 가능
코드
- 박스플롯
# column: 이상치 제거 대상 컬럼
Q1 = df[column].qunatile(0.25)
Q3 = df[column].qunatile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df = df[(df[column] >= lower_bound) & (df[column] <= upper_bound)]
4. 정규화/표준화: Scaling
필요 이유
- 모델(특히 거리 기반 알고리즘, 딥러닝 등)에 따라 특정 변수의 스케일이 크게 영향을 미칠 수 있음
- 변수 A의 범위는 0~1000, B의 범위는 0~1이라면, A가 B에 비해 모델에 더 큰 영향을 줌
정규화
- MinMaxScaler: 모든 값을 0과 1사이로 매핑
- 특징: 값의 스케일이 달라도 공통 범위로 맞출 수 있음
- 딥러닝, 이미지 처리 등에서 입력값을 0~1에서 제한해야 하거나 각 특성이 동일한 범위 내에 있어야 하는 경우
- 거리 기반 알고리즘(유클리디안 거리)이나 각 특성의 범위를 동일하게 맞춤으로써 계산 안정성을 높이고 싶은 경우
- 최소값/최대값이 이상치(Outlier)에 민감
- 만약 이상치가 있다면 대부분의 데이터가 구간 내부 한쪽에 치우침
- 새로운 데이터가 기존 최대값보다 커지거나, 최소값보다 작아지는 경우 스케일링 범위를 벗어날 수 있음
- 이 경우 재학습하거나 다른 처리 필요
- 특징: 값의 스케일이 달라도 공통 범위로 맞출 수 있음
표준화
- StandardScaler: 평균을 0, 표준편차를 1으로 만듦
- 특징: 분포가 정규분포에 가깝게 변형됨
- 평균이 0, 표준편차가 1로 맞춰지므로 정규분포 가정을 사용하는 알고리즘(선형회귀, 로지스틱회귀, SVM)에 자주 사용
- 변환된 값들이 이론적으로 -∞ ~ +∞ 의 범위를 가질 수 있음
- 통계적으로 99.9%의 값들이 -3 ~ 3의 범위를 가짐
- 데이터 분포가 심하게 치우쳐있으면(긴 꼬리 분포) 평균과 표준편차만으로는 충분한 스케일링이 되지 않을 수 있음
- 이 경우 로그 변환, RoburtScaler 등 추가 고려
- 특징: 분포가 정규분포에 가깝게 변형됨
코드
- MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
# 스케일링 적용할 컬럼
column = [column1, column2, ...]
# 객체 생성
mm_scaler = MinMaxScaler()
# 스케일링된 결과 데이터프레임으로 변환
df_scaled = pd.DataFrame(mm_scaler.fit_transform(df[column]), columns=column)
- StandardScaler
from sklearn.preprocessing import StandardScaler
# 스케일링 적용할 컬럼
column = [column1, column2, ...]
# 객체 생성
ss_scaler = StandardScaler()
# 스케일링된 결과 데이터프레임으로 변환
df_scaled = pd.DataFrame(ss_scaler.fit_transform(df[column]), columns=column)
5. 불균형 데이터 처리
문제점
- 편향 발생: 모델이 극도록 적은 클래스를 거의 예측하지 못할 가능성이 큼
해결 기법
- Oversampling: 소수 클래스의 데이터를 증가
- Random Oversampling: 소수 클래스의 데이터를 단순 복제하여 개수를 늘림
- 단순무식한 방식
- SMOTE(Synthetic Minority Over-sampling Technique): 비슷한 데이터들을 서로 섞어 새로운 데이터 생성
- 소수 클래스 안에서 가까운 데이터 몇개를 고르고 그 사이에 새 데이터 포인트를 만드는 방법
- Random Oversampling: 소수 클래스의 데이터를 단순 복제하여 개수를 늘림
- Undersampling: 다수 클래스의 데이터를 감소
- 데이터 손실의 위험
- 혼합 기법: SMOTE와 Undersampling 적절히 섞어 사용
코드
from imblearn.over_sampling import SMOTE
X = df.drop('defect', axis=1)
y = df['defect']
smote = SMOTE(random_state=42)
X_res, y_res = smote.fit_resample(X, y)
6. 범주형 데이터 변환
원-핫 인코딩(One-Hot Encoding)
- 범주형 변수를 해당 범주에 해당하면 1, 아니면 0으로 표현
- 장점: 범주 간 서열 관계가 없을 때 사용
- 단점: 범주가 증가하면 차원의 수가 증가
- 이러한 경우 필요한 몇 개의 범주만 표현하고 나머지는 하나로 표현
레이블 인코딩(Label Encoding)
- 범주를 숫자로 직접 매핑
- 단순하지만 모델이 숫자의 크기를 서열 정보로 잘못 해석할 가능성이 존재
코드
- One-Hot Encoding
# 변환할 변수: label
# 범주형 변수 변환 (원-핫 인코딩)
df = pd.get_dummies(df, columns=['label'])
- Label Encoding
- 보통 알파벳 순서로 부여
- 예시는 그냥 하였지만 순서 정보가 없으면 사용하면 안 됨
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
df['label'] = encoder.fit_transform(df['label'])
7. 피처 엔지니어링 개요
- 모델 성능 향상을 위해 기존 데이터를 변형, 조합해 새로운 특성(피처)을 만드는 작업
중요성
- 복잡한 데이터 구조 안에 존재하는 패턴 효과적으로 추출해 모델이 쉽게 학습
- 제조업에서는 센서 데이터 간 시계열적/물리적 관계를 반영하는 경우가 많음
- 금융에서의 데이터는 고객 신용도, 거래내역, 시장 지표 등 복잡하고 다양한 변수를 포함하는 경우가 많음
- 마케팅에서는 고객 행동 데이터(클릭, 구매기록, 웹사이트 체류 시간 등)와 고객 특성 데이터(나이, 지역, 관심 분야 등)를 통합해 피처를 만들어야 효과적인 캠페인 타겟팅, 고객 세분화, 개인화 추천이 가능한 경우가 많음
8. 피처 엔지니어링 실습 예시
파생 변수 생성
- 날짜 파생 변수: 측정 시간이 '2025-03-12 15:38:00'이라면 '월(2)', '요일(월=1), '시(10', '주말여부(0/1)' 등으로 분해
- 수치형 변수 조합: 온도와 습도가 있을 때, 새로운 피처 '온도 * 습도' 추가
- 두 변수의 상호작용이 불량 발생에 영향을 줄 수 있음
- 로그 변환, 제곱근 변환: 분포가 매우 치우친 변수에 로그 변환 적용해 정규성에 가까워지도록 조정
변수 선택(Feature Selection)
- 상관 관계: 두 변수 간 상관도가 높은 상황인 경우 다중공선성 의심
- 중복 정보가 클 수 있으므로 하나만 남기거나 둘 다 제거 고려
- VIF: 회귀분석에서 다중공선성 문제 파악할때 사용
- 어떤 변수 하나가 다른 변수들과 얼마나 겹치는지(상관이 큰지) 수치로 보여주는 지표
- 일정 기준을 넘으면 해당 변수 제거하거나 비슷한 변수들을 합치는 등의 방법으로 문제 해결
- 모델 기반 중요도(Feature Importance)
- 트리 기반 모델을 훈련 후 중요도가 낮은 변수 제거
변수 간 상호작용 추가
- 다항식/교차 항 생성 (ex_2차 다항식)
- 제조 공정에서 온도, 압력, 속도 등이 곱해져야 비로소 의미가 생기는 경우가 존재
추가
- 전처리와 피처 엔지니어링 과정에서 모든 선택은 도메인 지식과 EDA 결과를 함께 고려해야 함
- 각 단계별로 간단한 모델을 시험적으로 훈련해보고 성능 변화 체크하는 것이 좋음
- 범주형 변수가 100개, 200개로 엄청나게 많은 경우 고차원 원-핫 인코딩으로 인해 모델이 복잡해질 수 있으므로 Feature Hashing 같은 다른 기법 검토하거나, 발생 빈도가 낮은 범주는 기타로 통합하는 등의 방법을 사용해볼 수 있음
실습
- 라이브러리 호출
import numpy as np
import pandas as pd
# 스케일링, 폴리노미얼, 라벨인코딩, SMOTE
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.preprocessing import PolynomialFeatures, LabelEncoder
from imblearn.over_sampling import SMOTE
# VIF 계산용
from statsmodels.stats.outliers_influence import variance_inflation_factor
- 데이터 생성
# ----------------------------------------------------------
# 1) 임의 데이터프레임 생성
# - 수치형 변수 2개, 범주형 변수 1개, 날짜변수 1개, 타깃 변수(불균형) 1개
# ----------------------------------------------------------
np.random.seed(42)
N = 200
# 수치형 변수를 생성 (평균 50, 표준편차 10)
num1 = np.random.normal(loc=50, scale=10, size=N)
num2 = np.random.normal(loc=100, scale=20, size=N)
# 범주형 변수 (A, B, C 중 무작위)
cat_col = np.random.choice(['A', 'B', 'C'], size=N)
# 날짜변수(최근 200일 내 임의 날짜) 생성
date_rng = pd.date_range('2025-01-01', periods=N, freq='D')
print(date_rng)
#np.random.shuffle(date_rng) # 날짜 순서 랜덤화
# 불균형 타깃(1이 10%, 0이 90%라고 가정)
# - 실제로 1이 대략 20개, 0이 180개
target = np.random.choice([0,1], p=[0.9, 0.1], size=N)
# 데이터프레임 구성
df = pd.DataFrame({
'num1': num1,
'num2': num2,
'cat_col': cat_col,
'date_col': date_rng,
'target': target
})
- 결측치, 이상치 임의로 추가
# ----------------------------------------------------------
# 2) 결측치 추가 & 이상치(Outlier) 주입
# - 일부 값을 NaN으로 바꾸고, 극단값 몇 개 삽입
# ----------------------------------------------------------
# (2-1) 결측치 추가
missing_indices_num1 = np.random.choice(df.index, size=5, replace=False) # 5개 결측
missing_indices_num2 = np.random.choice(df.index, size=5, replace=False) # 5개 결측
missing_indices_cat = np.random.choice(df.index, size=3, replace=False) # 3개 결측
missing_indices_date= np.random.choice(df.index, size=2, replace=False) # 2개 결측
df.loc[missing_indices_num1, 'num1'] = np.nan
df.loc[missing_indices_num2, 'num2'] = np.nan
df.loc[missing_indices_cat, 'cat_col'] = np.nan
df.loc[missing_indices_date,'date_col'] = pd.NaT
# (2-2) 이상치(Outlier) 주입: num1, num2에 극단값
outlier_indices_num1 = np.random.choice(df.index, size=3, replace=False)
outlier_indices_num2 = np.random.choice(df.index, size=3, replace=False)
df.loc[outlier_indices_num1, 'num1'] = df['num1'].mean() + 8*df['num1'].std()
df.loc[outlier_indices_num2, 'num2'] = df['num2'].mean() + 10*df['num2'].std()
print("=== [원본 데이터 일부] ===")
print(df.head(10))
print()
>= low_num1) & (df_outlier_iqr['num1'] <= up_num1) &
(df_outlier_iqr['num2'] >= low_num2) & (df_outlier_iqr['num2'] <= up_num2)
]
print(f"=== [이상치 제거 전 shape] : {df_dropna.shape}")
print(f"=== [표준편차 기준 제거 후 shape] : {df_outlier_std.shape}")
print(f"=== [IQR 기준 제거 후 shape] : {df_outlier_iqr.shape}")
print()
- 결측치 처리
# ----------------------------------------------------------
# 3) 결측치 처리
# (3-1) 일부 행 제거, (3-2) 평균·중앙값 등으로 대체
# ----------------------------------------------------------
df_dropna = df.dropna() # 모든 컬럼 중 결측값이 있으면 제거
df_fillna = df.copy()
# 수치형은 열별 평균으로 대체 (mean)
df_fillna['num1'] = df_fillna['num1'].fillna(df_fillna['num1'].mean())
df_fillna['num2'] = df_fillna['num2'].fillna(df_fillna['num2'].mean())
# 범주형은 최빈값으로 대체 (mode)
most_freq_cat = df_fillna['cat_col'].mode().iloc[0]
df_fillna['cat_col'] = df_fillna['cat_col'].fillna(most_freq_cat)
# 날짜열은 제거하지 않고 그대로 둠(또는 임의 날짜로 대체 가능)
# -> 시연을 위해 NaT(결측)도 남겨둠
print("=== [결측치 제거 후 shape] ===")
print(df_dropna.shape)
print("=== [결측치 대체 후 shape] ===")
print(df_fillna.shape)
print()
- 이상치 제거
# ----------------------------------------------------------
# 4) 이상치 제거
# - (4-1) 표준편차 기준 (mu ± 3*std)
# - (4-2) IQR 기준
# ----------------------------------------------------------
df_outlier_std = df_dropna.copy()
mean_num1, std_num1 = df_outlier_std['num1'].mean(), df_outlier_std['num1'].std()
mean_num2, std_num2 = df_outlier_std['num2'].mean(), df_outlier_std['num2'].std()
# 임계값 설정: ±3σ
lower_num1, upper_num1 = mean_num1 - 3*std_num1, mean_num1 + 3*std_num1
lower_num2, upper_num2 = mean_num2 - 3*std_num2, mean_num2 + 3*std_num2
df_outlier_std = df_outlier_std[
(df_outlier_std['num1'] >= lower_num1) & (df_outlier_std['num1'] <= upper_num1) &
(df_outlier_std['num2'] >= lower_num2) & (df_outlier_std['num2'] <= upper_num2)
]
# (4-2) IQR 기반
df_outlier_iqr = df_dropna.copy()
Q1_num1 = df_outlier_iqr['num1'].quantile(0.25)
Q3_num1 = df_outlier_iqr['num1'].quantile(0.75)
IQR_num1 = Q3_num1 - Q1_num1
Q1_num2 = df_outlier_iqr['num2'].quantile(0.25)
Q3_num2 = df_outlier_iqr['num2'].quantile(0.75)
IQR_num2 = Q3_num2 - Q1_num2
low_num1 = Q1_num1 - 1.5*IQR_num1
up_num1 = Q3_num1 + 1.5*IQR_num1
low_num2 = Q1_num2 - 1.5*IQR_num2
up_num2 = Q3_num2 + 1.5*IQR_num2
df_outlier_iqr = df_outlier_iqr[
(df_outlier_iqr['num1'] >= low_num1) & (df_outlier_iqr['num1'] <= up_num1) &
(df_outlier_iqr['num2'] >= low_num2) & (df_outlier_iqr['num2'] <= up_num2)
]
print(f"=== [이상치 제거 전 shape] : {df_dropna.shape}")
print(f"=== [표준편차 기준 제거 후 shape] : {df_outlier_std.shape}")
print(f"=== [IQR 기준 제거 후 shape] : {df_outlier_iqr.shape}")
print()
- 스케일링(예외: 트리 기반 모델)
# ----------------------------------------------------------
# 5) 스케일링: 표준화(StandardScaler), 정규화(MinMaxScaler)
# - 예시로 df_outlier_iqr 를 사용
# ----------------------------------------------------------
df_scaled = df_outlier_iqr.copy()
scaler_std = StandardScaler()
scaler_minmax = MinMaxScaler()
df_scaled['num1_std'] = scaler_std.fit_transform(df_scaled[['num1']])
df_scaled['num2_std'] = scaler_std.fit_transform(df_scaled[['num2']])
df_scaled['num1_minmax'] = scaler_minmax.fit_transform(df_scaled[['num1']])
df_scaled['num2_minmax'] = scaler_minmax.fit_transform(df_scaled[['num2']])
print("=== [스케일링 결과 컬럼 확인] ===")
print(df_scaled[['num1','num1_std','num1_minmax','num2','num2_std','num2_minmax']].head())
print()
- 범주형 변환(예외: catboost)
# ----------------------------------------------------------
# 6) 범주형 데이터 변환 (원-핫, 라벨 인코딩)
# - 라벨 인코딩: cat_col
# - 원-핫 인코딩: cat_col (또는 라벨 인코딩 후 다른 DF에 적용 가능)
# ----------------------------------------------------------
df_cat = df_scaled.copy()
# (6-1) 라벨 인코딩
label_encoder = LabelEncoder()
df_cat['cat_label'] = label_encoder.fit_transform(df_cat['cat_col'])
# (6-2) 원-핫 인코딩
df_cat = pd.get_dummies(df_cat, columns=['cat_col'])
print("=== [라벨 인코딩 + 원핫 인코딩 결과 컬럼] ===")
print(df_cat.head())
print()
- 파생 변수 생성
# ----------------------------------------------------------
# 7) 파생변수 생성
# - (7-1) 날짜 파생: 연, 월, 요일, 주말여부 등
# - (7-2) 수치형 변수 조합
#
# ----------------------------------------------------------
df_feat = df_cat.copy()
# (7-1) 날짜 파생
df_feat['year'] = df_feat['date_col'].dt.year
df_feat['month'] = df_feat['date_col'].dt.month
df_feat['dayofweek'] = df_feat['date_col'].dt.dayofweek # 월=0, 화=1, ...
df_feat['is_weekend'] = df_feat['dayofweek'].apply(lambda x: 1 if x>=5 else 0)
# (7-2) 수치형 변수 조합 예시: num1 + num2
df_feat['num_sum'] = df_feat['num1_std'] + df_feat['num2_std']
print("=== [파생 변수 생성 결과] ===")
print(df_feat[['date_col','year','month','dayofweek','is_weekend','num1','num2','num_sum']].head())
print()
- 다중공선성 확인
# ----------------------------------------------------------
# 8) 다중공선성 확인 (상관관계, VIF)
# - 예시로 수치형 변수(num1, num2, num_sum, num1_log 등)만 확인
# ----------------------------------------------------------
df_corr = df_feat[['target', 'num1_std', 'num2_std', 'cat_label', 'year', 'month', 'dayofweek', 'is_weekend', 'num_sum']].dropna()
print("=== [상관계수] ===")
print(df_corr.corr())
# VIF 계산 함수
def calc_vif(df_input):
vif_data = []
for i in range(df_input.shape[1]):
vif = variance_inflation_factor(df_input.values, i)
vif_data.append((df_input.columns[i], vif))
return pd.DataFrame(vif_data, columns=['feature','VIF'])
vif_df = calc_vif(df_corr)
print("\n=== [VIF 결과] ===")
print(vif_df)
print()
- 불균형 데이터 처리
# ----------------------------------------------------------
# 9) 불균형 데이터 처리: SMOTE
# - 타깃이 [0,1]로 되어 있고, 1이 매우 적은 상태
# - SMOTE 적용 위해선 '피처'와 '타깃' 분리 필요
# ----------------------------------------------------------
df_smote = df_feat.copy().dropna(subset=['target']) # 일단 이상치,결측 제거된 DF 사용
X = df_smote[['num_sum', 'cat_label', 'year', 'month', 'dayofweek', 'is_weekend']] # 간단히 수치형 2개만 피처로
y = df_smote['target']
print("=== [SMOTE 전 레이블 분포] ===")
print(y.value_counts())
sm = SMOTE(random_state=42)
X_res, y_res = sm.fit_resample(X, y)
print("=== [SMOTE 후 레이블 분포] ===")
print(pd.Series(y_res).value_counts())
print()
'AI > 머신러닝' 카테고리의 다른 글
실무에 쓰는 머신러닝 기초 1주차 (분류) (0) | 2025.03.14 |
---|---|
실무에 쓰는 머신러닝 기초 1주차 (회귀) (2) | 2025.03.13 |
실무에 쓰는 머신러닝 기초 1주차 (머신러닝) (2) | 2025.03.12 |
시계열 다루기 2일차 (0) | 2025.02.27 |
시계열 다루기 1일차 (0) | 2025.02.26 |