머신 러닝 (4) 데이터 전처리
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
pima_columns = ['pregnancies', 'glucose', 'blood_pressure', 'skin_thickness',
'insulin', 'bmi', 'diabetes_pedigree_function', 'age', 'outcome']
pima_data_url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv'
# names 파라미터를 사용하여 컬럼명 지정
df = pd.read_csv(pima_data_url, names=pima_columns)
df.info()
display(df.describe())
# 0이 될 수 없는 피처 목록 (변경된 컬럼명 적용)
feature_columns = ['glucose', 'blood_pressure', 'skin_thickness', 'insulin', 'bmi']
# 1. '0' 값을 NaN으로 변환
print(f"변환 전 '0' 개수:\n{ (df[feature_columns] == 0).sum() }")
df[feature_columns] = df[feature_columns].replace(0, np.nan)
# 2. 평균값으로 NaN 대치 (Imputation)
imputer = SimpleImputer(strategy='mean')
df[feature_columns] = imputer.fit_transform(df[feature_columns])
print(f"\n대치(Imputation) 후 결측치 개수:\n{df[feature_columns].isnull().sum()}")
# outcome을 제외한 특성 데이터 선택
X = df.drop('outcome', axis=1)
# StandardScaler 객체 생성 및 적용
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 보기 쉽게 DataFrame으로 변환
X_scaled_df = pd.DataFrame(X_scaled, columns=X.columns)
print("스케일링 후 통계 요약 (평균은 0, 표준편차는 1에 근사):")
display(X_scaled_df.describe().round(2))
# 1. 'age' 피처로 'age_group' 생성 (Binning)
bins = [20, 30, 40, 50, 60, 70, 81]
labels = ['20대', '30대', '40대', '50대', '60대', '70대']
df['age_group'] = pd.cut(df['age'], bins=bins, labels=labels, right=False)
display(df)
# 2. 원-핫 인코딩 적용 (pd.get_dummies)
# 2. 원-핫 인코딩 적용 (pd.get_dummies)
df_encoded = pd.get_dummies(df, columns=['age'])
display(df_encoded)
age_cols = [col for col in df_encoded.columns if 'age_group' in col]
display(df_encoded[['age'] + age_cols].head())
5단계: 이상치 처리 (Outlier Handling)
주의 : 의학적으로 검증 필요
print("\n=== [5단계] 이상치 처리 (IQR Method) ===")
print(f"처리 전 데이터 형태: {df.shape}")
# blood_pressure 변수의 Q1, Q3 계산
Q1 = df['blood_pressure'].quantile(0.25)
Q3 = df['blood_pressure'].quantile(0.75)
IQR = Q3 - Q1
# 이상치 경계 계산
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(f"blood_pressure IQR: {IQR:.2f}")
print(f"Lower Bound: {lower_bound:.2f}, Upper Bound: {upper_bound:.2f}")
# 경계를 벗어나는 데이터 필터링 (정상 범위 데이터만 추출)
outliers_removed_df = df[
(df['blood_pressure'] >= lower_bound) &
(df['blood_pressure'] <= upper_bound)
]
print(f"처리 후 데이터 형태: {outliers_removed_df.shape}")
print(f"제거된 이상치 개수: {df.shape[0] - outliers_removed_df.shape[0]}")
df[(df['blood_pressure'] < lower_bound)| (df['blood_pressure'] > upper_bound)]
print(df[df['insulin']==0])
df[df['insulin']>600]
# 시각화를 위해 원본 데이터 복사 (전처리 과정에서 대치된 값 포함)
viz_df = df.copy()
# 로그 변환 적용 (np.log1p 사용) - insulin 컬럼 사용
# log(x)가 아니라 log1p를 사용하는 이유 x가 0일 때 log(0)은 -∞가 되기 때문
viz_df['insulin_log'] = np.log1p(viz_df['insulin'])
# 시각화 비교
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# 변환 전 분포
sns.histplot(viz_df['insulin'], ax=ax1, kde=True, color='#408AF0')
ax1.set_title('Original Insulin Distribution')
# 변환 후 분포
sns.histplot(viz_df['insulin_log'], ax=ax2, kde=True, color='#00A9B7')
ax2.set_title('Log-Transformed Insulin Distribution')
plt.tight_layout()
plt.show()
댓글
댓글 쓰기