멀티캠퍼스 데이터분석 5월 13일 수업 내용 — LogisticRegression으로 신체 능력 분류, SVM/SVR 이론 및 실습, outlier_iqr 함수 모듈화
📌 목차
- outlier_iqr() — 극단치 처리 함수 (모듈)
- LogisticRegression 실습 — bodyPerformance 데이터
- 범주형 데이터 변환 — 4가지 방법 비교
- 예측 확률 & decision_function 시각화
- 혼동 행렬 히트맵 시각화
- 불균형 데이터 처리 — class_weight & 샘플링
- 연습문제 — 다중 분류 & average 옵션
- SVM (서포트 벡터 머신) — 이론 & 실습
- SVR (서포트 벡터 머신 — 회귀) — 이론 & 실습
1. outlier_iqr() — 극단치 처리 함수 (모듈)
outlier.py 파일로 저장해두고 from outlier import outlier_iqr 로 불러와서 사용합니다.
import numpy as np
def outlier_iqr(data, *cols, n=1.5, drop=False):
"""
Parameters
----------
data : DataFrame — 원본 데이터프레임
*cols : str — 극단치를 확인할 컬럼명 (복수 가능)
n : float — 경계 범위 배수 (기본값 1.5)
drop : bool — True: 제거 / False: 경계값으로 대체 (기본값 False)
Returns
-------
df : 처리된 DataFrame
whis_dict: {컬럼명: 극단치 DataFrame} 딕셔너리
"""
df = data.copy()
whis_dict = {}
for col in cols:
try:
q_1, q_3 = np.percentile(df[col], [25, 75])
iqr = q_3 - q_1
upper_whis = q_3 + n * iqr
lower_whis = q_1 - n * iqr
print(f"""
컬럼: {col}
상단 경계: {upper_whis}
하단 경계: {lower_whis}
""")
upper_flag = df[col] > upper_whis
lower_flag = df[col] < lower_whis
print(f"상단 극단치: {upper_flag.sum()}개 / 하단 극단치: {lower_flag.sum()}개")
whis_dict[col] = df.loc[upper_flag | lower_flag]
if drop:
df = df.loc[~(upper_flag | lower_flag)]
else:
df.loc[upper_flag, col] = upper_whis
df.loc[lower_flag, col] = lower_whis
except Exception as e:
print(f'Error: {e}')
return df, whis_dict
💡 2026.05.06 수정 — 두 번째 매개변수를 col → *cols (가변 인자) 로 변경하여
여러 컬럼을 한 번에 처리할 수 있도록 개선되었습니다.
2. LogisticRegression 실습 — bodyPerformance 데이터
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
import warnings
warnings.filterwarnings('ignore')
body = pd.read_csv('../data/bodyPerformance.csv')
body.info()
body.describe()
컬럼 설명
| 컬럼 | 설명 |
|---|
| age | 나이 |
| gender | 성별 (문자열 — M/F) |
| height_cm | 키 |
| weight_kg | 무게 |
| body fat_% | 체지방률 |
| diastolic | 이완기 혈압 |
| systolic | 수축기 혈압 |
| gripForce | 악력 |
| sit and bend forward_cm | 앉아서 윗몸 앞으로 굽히기 |
| sit-ups counts | 윗몸 일으키기 |
| broad jump_cm | 제자리 멀리뛰기 |
| class | 신체 능력 등급 (A/B/C/D) |
3. 범주형 데이터 변환 — 4가지 방법 비교
body['gender'].map(lambda x: 0 if x == 'M' else 1)
body['gender'].map({'M': 0, 'F': 1})
body['gender'].replace('M', 0).replace('F', 1)
df = pd.get_dummies(body, columns=['gender'], drop_first=True)
np.where(body['gender'] == 'M', 0, 1)
target(class) 이진 변환 — A는 1, 나머지는 0
df['class_1'] = np.where(df['class'] == 'A', 1, 0)
df['class_1'].value_counts()
데이터 분할 & 학습
x = df.drop(['class', 'class_1'], axis=1)
y = df['class_1']
X_train, X_test, y_train, y_test = train_test_split(
x, y, test_size=0.3, random_state=42, stratify=y
)
logR = LogisticRegression()
logR.fit(X_train, y_train)
4. 예측 확률 & decision_function 시각화
proba = pd.DataFrame(logR.predict_proba(X_train))
cs = pd.DataFrame(logR.decision_function(X_train))
df2 = pd.concat([proba, cs], axis=1)
df2.columns = ['Not A', 'A', 'decision_function']
df2.sort_values('decision_function', inplace=True)
df2.reset_index(drop=True, inplace=True)
plt.figure(figsize=(16, 8))
plt.axhline(y=0.5, linestyle='--', color='black', linewidth=3, alpha=0.3)
plt.axvline(x=0, linestyle='--', color='black', linewidth=3, alpha=0.3)
plt.plot(df2['decision_function'], df2['Not A'], 'r--', label='Not A')
plt.plot(df2['decision_function'], df2['A'], 'b--', label='A')
plt.legend()
plt.xlabel('decision_function')
plt.ylabel('Proba')
plt.show()
💡 decision_function vs predict_proba
predict_proba() → 각 클래스별 확률값 (0~1)
decision_function() → 결정 경계와의 거리 (양수: 양성, 음수: 음성)
decision_function = 0 을 기준으로 양/음 분류
5. 혼동 행렬 히트맵 시각화
pred = logR.predict(X_test)
cm = confusion_matrix(y_test, pred)
acc = accuracy_score(y_test, pred)
prc = precision_score(y_test, pred)
rcll = recall_score(y_test, pred)
f1 = f1_score(y_test, pred)
plt.figure(figsize=(8, 8))
sns.heatmap(
cm, annot=True, cmap='Blues', fmt='d',
xticklabels=['pred Negative', 'pred Positive'],
yticklabels=['actual Negative', 'actual Positive']
)
plt.show()
print('정확도 :', round(acc, 2))
print('정밀도 :', round(prc, 2))
print('재현율 :', round(rcll, 2))
print('F1 :', round(f1, 2))
6. 불균형 데이터 처리 — class_weight & 샘플링
class_weight='balanced' — 모델 내부에서 가중치 부여
logR2 = LogisticRegression(class_weight='balanced')
logR2.fit(X_train, y_train)
pred_2 = logR2.predict(X_test)
print('정확도 :', round(acc, 2), round(accuracy_score(y_test, pred_2), 2))
print('정밀도 :', round(prc, 2), round(precision_score(y_test, pred_2), 2))
print('재현율 :', round(rcll, 2), round(recall_score(y_test, pred_2), 2))
print('F1 :', round(f1, 2), round(f1_score(y_test, pred_2), 2))
극단치 제거 후 재학습
from outlier import outlier_iqr
outlier_drop_df, outlier_dict = outlier_iqr(
df, *df.drop('class_1', axis=1).columns, drop=True
)
X_train, X_test, y_train, y_test = train_test_split(
outlier_drop_df.drop(['class', 'class_1'], axis=1),
outlier_drop_df['class_1'],
test_size=0.3,
stratify=outlier_drop_df['class_1']
)
logR2.fit(X_train, y_train)
pred_3 = logR2.predict(X_test)
print('정확도 :', round(accuracy_score(y_test, pred_3), 2))
print('정밀도 :', round(precision_score(y_test, pred_3), 2))
print('재현율 :', round(recall_score(y_test, pred_3), 2))
print('F1 :', round(f1_score(y_test, pred_3), 2))
언더 샘플링 & SMOTE — 공통 함수로 실험
from imblearn.under_sampling import RandomUnderSampler
from imblearn.over_sampling import SMOTE
def logR_function(x, y, weight=None):
X_train, X_test, y_train, y_test = train_test_split(
x, y, test_size=0.3, random_state=42, stratify=y
)
model = LogisticRegression(class_weight=weight)
model.fit(X_train, y_train)
pred = model.predict(X_test)
print('정확도 :', round(accuracy_score(y_test, pred), 2))
print('정밀도 :', round(precision_score(y_test, pred, average='macro'), 2))
print('재현율 :', round(recall_score(y_test, pred, average='macro'), 2))
print('F1 :', round(f1_score(y_test, pred, average='macro'), 2))
return pred
x = df.drop(['class', 'class_1'], axis=1)
y = df['class_1']
undersample = RandomUnderSampler(sampling_strategy=1)
x_under, y_under = undersample.fit_resample(x, y)
under_pred = logR_function(x_under, y_under)
smote = SMOTE(sampling_strategy=1)
x_over, y_over = smote.fit_resample(x, y)
over_pred = logR_function(x_over, y_over)
7. 연습문제 — 다중 분류 & average 옵션
문제
- gender 컬럼을 0, 1로 변환
- class 컬럼 → A:1, B:2, C:3, D:4로 변환
- 극단치 데이터를 경계값으로 대체
- LogisticRegression으로 혼동 행렬 및 성능 지표 확인
body['gender'] = body['gender'].map({'M': 0, 'F': 1})
body['class'] = body['class'].map({'A': 1, 'B': 2, 'C': 3, 'D': 4})
cols = body.drop('class', axis=1).columns
outlier_df, whis_dict = outlier_iqr(body, *cols)
x = outlier_df.drop('class', axis=1)
y = outlier_df['class']
pred = logR_function(x, y)
average 옵션 — 다중 분류 성능 지표
다중 분류에서는 precision_score, recall_score, f1_score 에 average 매개변수를 지정해야 합니다.
| average | 설명 |
|---|
'macro' | 각 클래스 점수의 단순 평균 — 모든 클래스를 동등하게 취급 |
'weighted' | 각 클래스의 샘플 수 비율로 가중 평균 |
'micro' | 혼동 행렬의 전체 합산으로 점수 생성 (이진 분류에서 정확도와 동일) |
혼동 행렬 시각화 — 다중 분류
datas = train_test_split(x, y, test_size=0.3, random_state=42, stratify=y)
cm = confusion_matrix(datas[3], pred)
plt.figure(figsize=(8, 8))
sns.heatmap(
cm, annot=True, cmap='Blues', fmt='d',
xticklabels=[1, 2, 3, 4],
yticklabels=[1, 2, 3, 4]
)
plt.ylabel('Actual Class')
plt.xlabel('Pred Class')
plt.show()
💡 train_test_split() 의 반환값 순서: X_train(0), X_test(1), y_train(2), y_test(3)
datas[3] → y_test
8. SVM (서포트 벡터 머신) — 이론 & 실습
- 데이터를 분리하는 최적의 결정 경계를 찾고 경계선에서 마진(여유 공간) 을 최대화
- 마진 밖 데이터에 패널티를 부여
- 실제 데이터가 선형이 아닌 경우 커널 함수를 이용하여 고차원 공간으로 매핑
주요 매개변수
| 매개변수 | 기본값 | 설명 |
|---|
C | 1.0 | 규제 강도의 역수 — 클수록 마진 좁아짐 (과적합 위험) |
kernel | 'rbf' | 커널 함수 ('linear'/'poly'/'rbf') |
gamma | - | 커널 폭 — 클수록 경계 복잡 (과적합), 작을수록 경계 유연 |
degree | 3 | 다항식 커널에서의 차수 |
probability | False | True 시 predict_proba() 사용 가능 (속도 저하) |
주요 속성 & 메서드
| 이름 | 설명 |
|---|
support_ | 서포트 벡터의 인덱스 |
support_vectors_ | 서포트 벡터의 실제 데이터값 |
n_support_ | 클래스별 서포트 벡터 개수 |
coef_ | 회귀 계수 (linear 커널만 사용 가능) |
decision_function() | 결정 함수값 (마진과의 거리) |
SVC 실습 — C 값 비교
import pandas as pd
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
df = pd.read_csv('../data/classification.csv')
sns.pairplot(data=df, hue='success')
x = df.drop('success', axis=1)
y = df['success']
X_train, X_test, y_train, y_test = train_test_split(
x, y, test_size=0.3, stratify=y, random_state=42
)
svc = SVC()
svc2 = SVC(C=0.5)
svc.fit(X_train, y_train)
svc2.fit(X_train, y_train)
pred = svc.predict(X_test)
pred2 = svc2.predict(X_test)
print('정확도 :', round(accuracy_score(y_test, pred), 2), round(accuracy_score(y_test, pred2), 2))
print('정밀도 :', round(precision_score(y_test, pred), 2), round(precision_score(y_test, pred2), 2))
print('재현율 :', round(recall_score(y_test, pred), 2), round(recall_score(y_test, pred2), 2))
print('F1 :', round(f1_score(y_test, pred), 2), round(f1_score(y_test, pred2), 2))
9. SVR (서포트 벡터 머신 — 회귀) — 이론 & 실습
- 입실론(ε) 튜브 안에 있는 데이터는 오차로 보지 않음
- 튜브 밖 데이터에만 패널티를 부여
주요 매개변수
| 매개변수 | 설명 |
|---|
kernel | 'linear'/'rbf'/'poly' |
epsilon | 오차 허용 폭 (튜브 크기) |
C | 규제 강도 역수 |
gamma | 커널 폭 (rbf, poly에서 사용) |
degree | 다항식 차수 (poly에서 사용) |
max_iter | 최대 반복 횟수 (수치 불안정 시 사용) |
tol | 수렴 판단 기준 (기본값 0.001) |
SVR 실습 — 커널 종류별 성능 비교
import numpy as np
from sklearn.svm import SVR
from sklearn.metrics import mean_squared_error, r2_score
x = np.sort(5 * np.random.rand(40, 1), axis=0)
y = np.sin(x)
y[::5] += 3 * (0.5 - np.random.rand(len(y[::5]), 1))
svr_rbf = SVR(kernel='rbf', gamma='auto', epsilon=0.1)
svr_lin = SVR(kernel='linear')
svr_poly = SVR(kernel='poly', gamma='auto')
svr_rbf.fit(x, y)
svr_lin.fit(x, y)
svr_poly.fit(x, y)
pred_rbf = svr_rbf.predict(x)
pred_lin = svr_lin.predict(x)
pred_poly = svr_poly.predict(x)
index = ['RBF', 'Linear', 'Poly']
result = pd.DataFrame(index=index, columns=['MSE', 'R2'])
for pred, idx in zip([pred_rbf, pred_lin, pred_poly], index):
result.loc[idx, 'MSE'] = round(mean_squared_error(y, pred), 2)
result.loc[idx, 'R2'] = round(r2_score(y, pred) * 100, 2)
result
커널 종류별 사용 가능 매개변수
| 커널 | 사용 가능 매개변수 |
|---|
linear | C, epsilon |
rbf | C, epsilon, gamma |
poly | C, epsilon, gamma, degree |
📎 핵심 개념 요약
| 개념 | 설명 |
|---|
LogisticRegression | 분류 모델 — 확률값으로 클래스 판단 |
predict_proba() | 클래스별 예측 확률 반환 |
decision_function() | 결정 경계와의 거리 반환 |
class_weight='balanced' | 불균형 데이터에 자동 가중치 부여 |
np.where(조건, 참, 거짓) | 조건에 따라 값 선택 (벡터화) |
average='macro' | 다중 분류 — 클래스별 점수 단순 평균 |
average='weighted' | 다중 분류 — 샘플 수 비율로 가중 평균 |
average='micro' | 다중 분류 — 혼동 행렬 전체 합산 |
| SVC | 서포트 벡터 머신 (분류) |
| SVR | 서포트 벡터 머신 (회귀) |
C (SVM) | 규제 강도 역수 — 클수록 마진 좁아짐 |
kernel='rbf' | 가우시안 커널 — 비선형 분류에 가장 많이 사용 |
epsilon (SVR) | 오차 허용 폭 (튜브 크기) |
support_vectors_ | 서포트 벡터의 실제 데이터값 |
datas[3] | train_test_split() 반환값 중 y_test |