데이터 로드 및 기본 정보 확인
# 데이터 전처리 및 시각화 라이브러리 설치
import pandas as pd
import numpy as np
import math
from scipy import stats
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib as mpl
%matplotlib inline
# 그래프 상세 설정
plt.style.use('seaborn')
plt.rc('font', family="AppleGothic")
mpl.rcParams['axes.unicode_minus'] = False # 마이너스 부호 깨짐 설정
# 경고 무시
import warnings
warnings.filterwarnings('ignore')
Python
복사
# 데이터 로딩
df = pd.read_csv('파일경로')
df.head()
df.tail()
# 데이터 기본 정보 확인
df.info()
df.describe()
Python
복사
# 타겟 변수 설정
y =
# 타겟 변수 분포 확인
counts = df.y.value_counts(normalize=True).sort_index() # 분류 문제인 경우
# seaborn
################## 1. 분류 문제 #########################
ax = sns.countplot(x = counts)
ax.set_title('')
ax.set_xlabel('class')
ax.set_ylabel('count')
# plt 로 시각화할 경우,
fig, ax = plt.subplots(figsize=(6,4))
counts.plot(kind='bar', ax=ax)
ax.set_title('')
ax.set_xlabel('class')
ax.set_ylabel('count')
# Imbalance 지표
maj = counts.max()
min = counts.min()
print(f'총 샘플 수: {counts.sum()}')
print(f'가장 많은 클래스 비율: {maj/counts.sum():.2%}')
print(f'가장 적은 클래스 비율: {min/counts.sum():.2%}')
print(f'Imbalance Ratio (maj/min): {maj/min:.2f}')
print('\n클래스별 비율:')
print((counts/counts.sum()*100).round(2).astype(str) + '%')
################## 2. 회귀 문제 #########################
# 히스토그램 + 박스플롯
# plt
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12,4))
ax1.set_title('Histogram')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')
ax2.boxplot(df.y, vert=False)
ax2.set_title('Boxplot')
ax2.set_xlabel('Value')
# 왜도 , 첨도
skewness = df.y.skew()
kurtosis = df.y.kurtosis()
print(f'왜도 (skewness): {skewness:.2f}')
print(f'첨도 (kurtosis): {kurtosis:.2f}')
# Q-Q plot 정규성 검토
plt.figure(figsize=(5,5))
stats.probplot(df.y, dist='norm', plot=plt)
plt.title('Q-Q plot')
Python
복사
# 상관 계수 확인
corr = df.corr()
corr['타겟변수'].sort_values(ascending=True) # 타겟변수와의 상관관계 확인
# 상관관계 시각화 : sns.heatmap
plt.figure(figsize=(10,8))
sns.heatmap(corr, annot=True, center=0)
# 변수들간의 관계 시각화 : 선형 관계 확인 가능
sns.pairplot(df)
Python
복사
Simple EDA + Processing
전처리 하는 부분은 test 데이터셋에도 적용해줘야 하는 것 잊지 말기
전처리 내용들 함수에 추가해주기
numeric_cols = df.select_dtypes(include=['int','float']).columns
cat_cols = df.select_dtypes(include=['object','bool']).columns
Python
복사
# 수치형 변수 분포 시각화
# 주의할 점 : int, float 라고 해서 모두 수치형 변수인 것은 아님! -> 꼭 확인해볼 것
df[numeric_cols].hist()
Python
복사
# 왼쪽으로 치우친 변수 변환 (positive skewed)
# 로그 변환 (0 또는 음수값이 있을 때는 사용 X, 비율 변수도 사용 X)
# 0만 포함되었을 경우, 1을 더해줌(아주 작은 값)으로써 로그 변환 적용 가능)
# 가격 데이터에 많이 사용되는 변환 방식
np.log(df['해당 변수'])
# 오른쪽으로 치우친 변수 변환 (negative skewed)
# 제곱근 변환
np.sqrt(df['해당 변수'])
Python
복사
Imputate Missing Value
데이터 분석 테스트에서는 시간이 짧기 때문에 보통 결측치가 있을 경우, 많지 않다.
따라서, 삭제하지 말고 대체하는 방법을 고려하자.
0.
Do Nothing
그대로 놔두기. 일부 알고리즘은 알아서 결측치를 파악하여 해결하는 경우가 있다.(XGBoost)
또 어떤 알고리즘은 아예 결측값을 무시하는 경우도 있다. (LightGBM)
물론 대부분의 모델이 에러를 발생시키긴 한다.
1.
기술 통계량으로 대체하기
: 평균(mean()), 중앙값(median()), 최빈값(mode()) 등
# 평균과 중앙값은 수치형만 가능
# 데이터 샘플이 많지 않은 경우에 적합
df['f1'] = df.f1.fillna(df.f1.mean())
# sklearn 의 SimpleImputer 전처리 모델
from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer(strategy='mean') # median(중앙값), most_frequent(최빈값),
imp_mean.fit(train)
imputed_train_df = imp_mean.transform(train)
Python
복사
•
변수 간 상관관계를 고려하지 않는다.
•
카테고리 변수에는 부적합
•
정확하지는 않다
2.
bfill, ffill
시계열 데이터라면 앞 뒤데이터로 대체하기
3.
기타 알고리즘 활용 : KNN
과제 테스트에는 시간이 없으므로 위의 방식을 선택하는 것을 추천
3-1. KNN
분류에 사용하는 모델인데, 예측하기 위해서 변수 유사도를 사용한다. 새로운 데이터 포인트는 학습 데이터셋의 포인트 위치와 얼마나 가까운가에 따라 예측값을 할당받는다.
KNN 알고리즘을 결측값 채우기에 사용하는 간단한 Impyute 라이브러리 사용 예제는 다음과 같다
import sys
from impyute.imputation.cs import fast_knn
sys.setrecursionlimit(100000)
# start the KNN training
imputed_training = fast_knn(train.values, k=30)
Python
복사
KNN 알고리즘은 기본적인 평균 대체값을 생성하여 만들어지는 완전한 리스트(complete list)를 사용하여 KDTree를 구축한다. 그 다음에 KDTree를 사용해서 가장 가까운 이웃 데이터들(Nearest Neighbours, NN)이 무엇인지 계산한다. k-NN을 찾아내면 알고리즘은 이웃 데이터들의 가중평균을 취한다.
•
기본 통계량으로 대체하는 것보다 더 정확한 결과를 얻을 수 있다. (항상 그런 것은 아님)
•
컴퓨팅 리소스가 많이 든다. KNN은 학습할 때 통째로 올려서 계산하는 알고리즘이기 때문
•
SVM와 달리 데이터에 존재하는 아웃라이어(이상치)에 꽤 민감(취약)하다.
3-2. Imputation Using Multivariate Imputation by Chained Equation (MICE)
이러한 종류의 결측값 대체법은 결측값을 여러 번 반복하여 채우는 방식으로 수행한다. 다중대체법(Multiple Imputations, MIs)은 더 나은 방식으로 결측값의 불확실성을 측정하기 때문에 대체를 1회 수행하는 것보다 훨씬 좋다.
관련 논문
3-3. Imputation Using Deep Learning (Datawig)
이 방법은 카테고리 변수와 숫자값이 아닌 변수들에도 잘 작동한다. Datawig는 심층 신경망(DNN)을 사용해서 데이터프레임에 존재하는 결측값을 채우도록 머신러닝 모델을 훈련시키는 라이브러리다. 머신러닝 학습에 CPU와 GPU 모두를 지원한다.
import datawig
df_train, df_test = datawig.utils.random_split(train)
# Initialize a SimpleImputer model
imputer = datawig.SimpleImputer(
input_columns=['1','2','3','4','5','6','7', 'target'],
output_columns = '0',
output_path = 'imputer_model'
)
imputer.fit(train_df=df_train, num_epochs=50)
imputed = imputer.predict(df_test)
Python
복사
•
다른 대체법보다 상당히 정확
•
Feature Encoder 등 카테고리 변수를 핸들링할 수 있는 함수들을 제공
•
CPU, GPU 지원
•
데이터 사이즈가 클 경우, 상당히 오래 걸릴 수 있다
•
결측값을 채워넣을 타겟 칼럼과 상관성이 높거나 타겟 칼럼의 정보를 포함하고 있는 다른 컬럼들을 직접 지정해줘야 한다.
3-4. Other Imputation Models
•
Stochastic Regression Imputation
•
Extrapolation and Interpolation
•
Hot-Deck Imputation
결론적으로, 데이터셋에 존재하는 결측값을 대체하는 완벽한 방법은 없다. 각각의 전략은 특정한 데이터셋과 특정한 결측값 자료형에서 기대 이상으로 작동할 수 있지만, 반대로 형편없이 작동할 수도 있다. 어떤 결측값 자료형에 어떤 전략을 택해야 하는가에 대해서는 일종의 경험칙이 존재하지만, 그보다 당신이 직접 실험하고 어떤 모델이 어떤 데이터셋에 잘 작동하는지 직접 확인하는 것이 좋다.
4.
새로운 값으로 대체하기
결측치 수가 70% 이상으로 매우 많고, 제거하기에는 타겟 변수의 범주별로 차이가 극명하게 나타날 때는
‘unknown’ 등의 아예 다른 문자열로 채우고 이에 대한 설명을 적을 것
참고로 dtypes 가 category 일 경우, 채워지지 않을 수도 있는데 해당 변수의 데이터 타입을 object로 바꿔준 다음 값을 채워줄 것!
df.astype({'f1':'object'})
Python
복사
Encoding, Scaling
Encoding
카테고리형 데이터를 인코딩할 경우에는 대표적으로 두 가지 방법이 있다.
•
원핫 인코딩
신경망 계열 모델 활용 시에 유용
•
라벨 인코딩
트리 계열 모델 활용 시에 유용
from sklearn.preprocessing import OneHotEncoder, LabelEncoder, StandardScaler, MinMaxScaler
# 라벨 인코딩 예시
label_cols = ['embarked','deck','embark_town']
l_encoder = LabelEncoder()
for i in label_cols:
df[i] = l_encoder.fit_transform(df[i])
# 스케일링은 train/test 에 각각 다른 함수가 적용
# X, y 분리
X = df.drop('Survived', axis=1)
y = df[['Survived']]
from sklearn.moel_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(X,y, test_size=0.8, random_state=42)
scaler = StandardScaler()
X_train_std = scaler.fit_transform(X_train)
X_val_std = scaler.transform(X_val)
X_test_std = scaler.transform(X_test)
Python
복사
Scaling
대표적인 스케일링 모델 : StandardScaler, MinMaxScaler
•
train 데이터셋에는 fit_transform
•
valid, test 데이터셋에는 transform
상황 | 스케일러 추천 |
특성들이 대체로 정규분포를 따르고, 평균 0, 단위 분산이 필요할 때 | StandardScaler |
거리 기반 알고리즘에서 변수별 분산 영향 최소화 필요할 때 | StandardScaler |
값의 범위를 [0,1] 등으로 엄격히 제한해야 할 때 | MinMaxScaler |
딥러닝 모델(특히 sigmoid/Tanh 사용 시) 입력값 정규화가 중요할 때 | MinMaxScaler |
분포 형태는 유지하되, 일괄적인 스케일링이 필요할 때 | MinMaxScaler |
두 스케일링 모두 이상치에 기본적으로 취약하기 때문에 이상치가 많은 데이터의 경우에는 RobustScaler(중앙값, IQR 사용)를 고려하는 것이 좋다
베이스라인 모델 구축
•
hard voting
각 모델의 예측(predict) 결과(분류 레이블) 중 다수결로 최종 레이블 결정
•
soft voting
각 모델의 predict_proba() 결과(분류 레이블별 확률)를 평균낸 뒤, 가장 높은 확률의 레이블을 최종 레이블로 결정
# 분류
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier, GradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
# 회귀
from sklearn.linear_model import LinearRegression, Lasso, Ridge
from sklearn.ensemble import RandomForestRegressor, AdaBoostRegressor, GradientBoostingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.svm import SVR
# 평가 모델 - 분류
from sklearn.metrics import accuracy_score, f1_score, confusion_matrix, \
classification_report
# 평가 모델 - 회귀
from sklearn.metrics import mean_squared_error, mean_absolute_error, mean_absolute_percentage_error,\
r2_score
# 하이퍼파리미터 튜닝을 위한 모델
from sklearn.model_selection import RandomizedSearchCV, GridSearchCV
Python
복사
# 각 모델 정의
lr = LogisticRegression()
rf = RandomForestClassifier()
ada = AdaBoostClassifier()
gb = GradientBoostingClassifier()
dt = DecisionTreeClassifier
svc = SVC()
# hard voting, soft voting 비교
voting = VotingClassifier(
estimators=[('lr', lr),('dt', dt),('svc', svc)],
voting='hard' # 또는 'soft'
)
voting.fit(X_train, y_train)
y_voting = voting.predict(X_test)
print('validation accuracy: ', accuracy_score(y_test, y_voting))
# 모델 튜닝 예시
## 랜덤포레스트의 하이퍼파라미터 튜닝
params = {
'n_estimators' : [100,200,300],
'min_samples_split' : [2,3,4],
'criterion' : ['gini', 'entropy', 'log_loss'],
'max_features' : ['sqrt', 'log2', None]
}
rf_cv = RandomizedSearchCV(rf, params, n_iter=10, random_state=42)
rf_cv = rf_cv.fit(X_train, y_train)
y_rf_cv = rf_cv.predict(X_test)
print(rf_cv.best_params_)
print(accuracy_score(y_test, y_rf_cv))
Python
복사
•
튜닝은 필수.
•
상관관계가 낮다고 무턱대고 피처를 삭제하면 안된다.
•
세세한 EDA 전 딱봐도 상관 없는 피처 제거(인덱스, 아이디), 결측치 처리, 범주형 변수 인코딩, 문자열 처리, 스케일링 정도만 하고 모델링 바로 수행 > 베이스라인 성능 확인하고 EDA 추가할 것
•
모델링 후 앙상블 꼭 해보기 (시간이 되면)
•
주석과 설명을 많이 추가하자
•
plot 많이 그려보기
•
처음부터 무리하게 피처 엔지니어링하지 말고 베이스라인 모델 구축을 우선으로 하자
•
생각보다 전처리할 요소 없이 깔끔한 데이터가 많다.