머신 러닝 (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()

댓글

이 블로그의 인기 게시물

베이스 캠프에서 (1)

베이스 캠프에서 (2)

Database 분석 (4)