라이브러리 불러오기
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.impute import KNNImputer
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
데이터 불러오기
titanic = sns.load_dataset('titanic')
도메인 데이터를 활용한 결측치 처리
def fill_age_based_on_rules(df):
df['fare_bin'] = pd.qcut(df['fare'], q=4, labels=['low', 'mid-low', 'mid-high', 'high'])
age_means = df.groupby(['sex', 'pclass', 'fare_bin'], observed=False)['age'].mean()
def fill_age(row):
if np.isnan(row['age']):
group_mean = age_means.get((row['sex'], row['pclass'], row['fare_bin']))
return group_mean if group_mean == np.nan else df['age'].mean()
return row['age']
df['age'] = df.apply(fill_age, axis=1)
df.drop(columns=['fare_bin'], inplace=True)
return df
titanic_cleaned = fill_age_based_on_rules(titanic)
sex
, pclass
, fare_bin
의 조합을 통해 그룹별로 나이의 평균값을 계산하고, 결측된 나이를 해당 그룹의 평균으로 대체한다.독립변수와 종속변수 분리
X = titanic_cleaned[['pclass', 'age', 'sibsp', 'parch', 'fare', 'sex', 'embarked', 'class']]
y = titanic_cleaned['survived']
범주형 변수에 원-핫 인코딩 적용
X = pd.get_dummies(X, drop_first=True)
(참고)
열을 명시해주지않아도 범주형 변수에 대해 자동으로 인코딩 적용함drop_first=True
: 원-핫 인코딩 결과에서 첫 번째 범주를 제거하여 다중공선성 문제를 방지한다.학습 및 테스트 데이터로 분할
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
모델 생성 및 평가
# 랜덤포레스트 모델 생성 및 훈련
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)
# 성능 평가
def evaluate_model(y_true, y_pred):
accuracy = accuracy_score(y_true, y_pred)
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)
return accuracy, precision, recall, f1
train_metrics = evaluate_model(y_train, y_train_pred)
test_metrics = evaluate_model(y_test, y_test_pred)
metrics_df = pd.DataFrame({
"Train": train_metrics,
"Test": test_metrics
})
metrics_df
Metric | Train | Test | |
---|---|---|---|
0 | Accuracy | 0.980337 | 0.815642 |
1 | Precision | 0.988462 | 0.797101 |
2 | Recall | 0.958955 | 0.743243 |
3 | F1 Score | 0.973485 | 0.769231 |
30
대신 300
으로 입력정규분포의 특성
이상치 탐지 기준
일반적으로 평균으로부터 2~3표준편차 이상 떨어진 데이터를 이상치로 간주 (68-95-99.7 Rule
)
방법 | 기준 값 | 특징 | 적용 데이터 |
---|---|---|---|
정규분포 기반 | 평균 ± k × 표준편차 (보통 k=2~3) | 정규분포 가정, 간단한 계산 | 정규분포 데이터에 적합 |
MAD 기반 | 중앙값 ± k × MAD (보통 k=1.5~3) | 이상치에 강건함, 중앙값 중심 | 정규/비정규분포 모두 가능 |
IQR 기반 | Q1 - 1.5×IQR, Q3 + 1.5×IQR | 사분위수를 기준으로 이상치 탐지 | 정규/비정규분포 모두 가능 |
Isolation Forest | 평균 분리 깊이 (결정 트리 기반) | 랜덤 샘플링과 분리 깊이 기준 이상치 탐지 | 대규모 데이터, 고차원 데이터 적합 |
DBSCAN | 반경 ϵϵ 내 밀도와 데이터 수 | 밀도 기반, 클러스터 식별 및 이상치 탐지 | 비선형, 밀집 데이터에 적합 |
LOF (Local Outlier Factor) | 지역 밀도와 이웃 밀도 비교 점수 (LOF) | 주변 밀도 대비 상대적 밀도로 이상치 탐지 | 밀도가 다양한 데이터에 적합 |
슈하르트 (Shewhart Control Charts)
STL Decomposition을 이용한 이상치 탐지
ARIMA를 이용한 이상치 탐지
💡 도메인 지식을 통한 Outlier 선정의 중요성
- 특정 데이터가 이상치로 보이더라도, 실제 의미 있는 값일 수 있다.
- 반대로 정상 범위 내에 있어도 도메인 관점에서 비정상적인 데이터일 수 있다.
- 따라서 도메인 지식은 통계적 방법을 보완하여 보다 정확하고 의미 있는 이상치 선정을 가능하게 한다.
💡 목표 : IQR과 IsolationForest를 이용한 이상치 탐지하기
🧪 IQR을 이용한 이상치 탐지
라이브러리 불러오기
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
데이터 불러오기
tips = sns.load_dataset('tips')
data = tips[['total_bill', 'tip', 'size']]
히스토그램 및 박스플롯 그리기
fix, axes = plt.subplots(3, 2, figsize=(12, 6))
for i, column in enumerate(data.columns):
sns.histplot(data[column], bins=30, ax = axes[i,0], kde=True, color='blue')
axes[i,0].set_title(f'Hist {column}')
sns.boxplot(x=data[column], ax=axes[i,1], color='orange')
axes[i,1].set_title(f'box {column}')
fix.tight_layout()
plt.show()
이상치 출력
def detect_outliers_iqr(df, column):
Q1 = np.percentile(df[column], 25) # 1사분위수
Q3 = np.percentile(df[column], 75) # 3사분위수
IQR = Q3 - Q1 # IQR 계산
lower_bound = Q1 - 1.5 * IQR # 이상치 하한
upper_bound = Q3 + 1.5 * IQR # 이상치 상한
return df[(df[column] < lower_bound) | (df[column] > upper_bound)]
outliers_iqr = pd.concat([detect_outliers_iqr(data, col).assign(Column=col) for col in data.columns]).reset_index()
outliers_iqr = outliers_iqr.set_index('Column')
outliers_iqr
이상치가 제거된 데이터셋 출력
def remove_outliers_iqr(df, columns):
total_rows = len(df)
outlier_indices = []
# 모든 열에 대해 이상치 탐지
for column in columns:
outliers = detect_outliers_iqr(df, column)
outlier_indices.extend(outliers.index.tolist())
# 중복된 인덱스 제거
outlier_indices = list(set(outlier_indices))
# 이상치 제거
df_cleaned = df.drop(index=outlier_indices)
# 제거된 이상치 개수 출력
removed_count = len(outlier_indices)
print(f"전체 데이터 중 {removed_count}개의 이상치가 제거되었습니다.")
print(f"이상치 제거 후 데이터 개수: {len(df_cleaned)}개")
return df_cleaned
data_cleaned = remove_outliers_iqr(data, data.columns).reset_index(drop=True)
data_cleaned
🧪 IsolationForest를 이용한 이상치 탐지
라이브러리 불러오기
from sklearn.ensemble import IsolationForest
이상치 탐지 모델 생성 및 학습
iso = IsolationForest(contamination = 0.05, random_state=42)
outlier_flags = iso.fit_predict(data)
contamination
: 이상치로 간주할 데이터 포인트의 비율이상치 출력
outliser_iso = data[outlier_flags==-1]
outliser_iso
이상치 제거 후 데이터 출력
data_cleaned = data[outlier_flags == 1].reset_index(drop=True)
data_cleaned
범주형 변수 이상치 처리 기법 예시