데이터 분석가:Applied Data Analytics/판다스 데이터분석

데이터분석머신러닝-실습(KNN, SVM)

데이터분석 2025. 2. 17. 15:38
320x100
728x90

해당내용은 정보문화사 파이썬 머신러닝 판다스 데이터 분석 교재를 기준으로 했으며, 책 소개는 아래를 참고하시기 바랍니다.

 

데이터분석가 관련 도서 https://nesaram-health-1story.tistory.com/136

 

데이터 분석가 관련 추천도서

데이터 분석가는 보고서작성, 시각화, 통찰력, 비즈니스/도메인 지식의 능력이 필요하다.여기서 비즈니스/도메인 지식은 특정 산업이나 회사의 주요 활동 영역에 대한 이해를 말하며, 쉽게 말해

nesaram-health-1story.tistory.com

# 기본 라이브러리 불러오기
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

'''
[Step 1] 데이터 준비 - Seaborn에서 제공하는 titanic 데이터셋 가져오기
'''

# load_dataset 함수를 사용하여 데이터프레임으로 변환
df = sns.load_dataset('data/titanic')

df.head()

'''
[Step 2] 데이터 탐색
'''

# 데이터 자료형 확인
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   survived     891 non-null    int64  
 1   pclass       891 non-null    int64  
 2   sex          891 non-null    object 
 3   age          714 non-null    float64
 4   sibsp        891 non-null    int64  
 5   parch        891 non-null    int64  
 6   fare         891 non-null    float64
 7   embarked     889 non-null    object 
 8   class        891 non-null    object 
 9   who          891 non-null    object 
 10  adult_male   891 non-null    bool   
 11  deck         203 non-null    object 
 12  embark_town  889 non-null    object 
 13  alive        891 non-null    object 
 14  alone        891 non-null    bool   
dtypes: bool(2), float64(2), int64(4), object(7)
memory usage: 92.4+ KB
# 데이터 통계 요약정보 확인
df.describe()   #fare 값의 최대값이 512로 평균이나 중앙값에 비해 매우 큰값으로 검토필요

# 데이터 통계 요약정보 확인 (범주형)
df.describe(include='object')

# 누락 데이터 확인
df.isnull().sum()   # age 177개 누락, deck 688개 누락값 으로 결측치 처리 필요
survived         0
pclass           0
sex              0
age            177
sibsp            0
parch            0
fare             0
embarked         2
class            0
who              0
adult_male       0
deck           688
embark_town      2
alive            0
alone            0
dtype: int64
# 중복 데이터 확인
df.duplicated().sum()  #107개 행이 중복으로 제거 필요
107
# 목표변수 가 생존여부이므로 목표변수의 분포확인이 중요하다
df['survived'].value_counts()
survived
0    549
1    342
Name: count, dtype: int64
# 목표변수 - 시각화
sns.countplot(data=df, x='survived');  #생존자(1) 비율이 낮다 불균형문제대응:오버샘플링, 언더샘플링

# 시각화
g = sns.FacetGrid(df, col='survived', row='pclass', hue='sex')
g.map(sns.kdeplot, 'age', alpha=0.5, fill=True)
g.add_legend();   #1등석에선 남성이 여성보다 생존률이 낮고 2등석에선 여성이 생존율이 높다

# 시각화  동승한 부모 및 자식의 수가 3명 이하일때가 생존율이 높다
sns.displot(x='sibsp', kind='hist', hue='survived', data=df, multiple='fill');

# 시각화 동승자가 5명이상인 경우 생존자가 없다. 4명이하는 생존자가 있다.
sns.displot(x='parch', kind='hist', hue='survived', data=df, multiple='fill');

# 시각화
sns.boxplot(x='embarked', y='age', hue='survived', data=df);

'''
[Step 3] 데이터 전처리
'''

# 중복 데이터 제거
print('중복 제거 이전: ', df.shape)
df = df.drop_duplicates()
print('중복 제거 이후: ', df.shape)
중복 제거 이전:  (891, 15)
중복 제거 이후:  (784, 15)
# NaN값이 많은 deck 열을 삭제, embarked와 내용이 겹치는 embark_town 열을 삭제
rdf = df.drop(['deck', 'embark_town'], axis=1)  
rdf.columns.values
array(['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'fare',
       'embarked', 'class', 'who', 'adult_male', 'alive', 'alone'],
      dtype=object)
# age 열에 나이 데이터가 없는 모든 행을 삭제 - NaN 값 DataFrame에서'age'열에 결측값(NaN)이 있는 행 모두 제거하는 기능
# how='any''age' 열에서 NaN이 하나라도 존재하면 해당 행을 삭제 axis=0 (기본값) 행을 기준으로 제거
rdf = rdf.dropna(subset=['age'], how='any', axis=0)
print(len(rdf))
678
# embarked 열의 NaN값을 승선도시 중에서 가장 많이 출현한 값으로 치환하기
#  'embarked' 열에서 가장 많이 등장한 값(최빈값, Mode)을 찾는 기능
most_freq = rdf['embarked'].value_counts(dropna=True).idxmax()   #dropna=True 옵션은 NaN(결측값)을 무시하고 계산
print(most_freq)
S
# mode 메소드 활용 (최빈값) 'embarked' 열에서 가장 많이 등장한 값(최빈값, Mode)을 찾는 기능
most_freq2 = rdf['embarked'].mode()[0]   # mode() 최빈값(Mode, 가장 많이 등장한 값)을 리스트 형태로 반환
print(most_freq2)
S
# describe 메소드 활용 (최빈값)
# describe() 함수는 데이터의 기초 통계 요약을 제공하는 함수로, 기본적으로 숫자형(수치형) 데이터에 대한
# 통계를 출력한다.하지만 include='object' 옵션을 추가하면 문자열(범주형) 데이터의 통계를 출력할 수 있다.
rdf.describe(include='object')

# 최빈값으로 누락 데이터 치환하기 'embarked' 열에서 결측값(NaN)을 최빈값(most_freq)으로 채우는 역할
rdf['embarked'] = rdf['embarked'].fillna(most_freq)

# 결측치 확인하기
rdf.isnull().sum()
survived      0
pclass        0
sex           0
age           0
sibsp         0
parch         0
fare          0
embarked      0
class         0
who           0
adult_male    0
alive         0
alone         0
dtype: int64
'''
[Step 4] 변수 선택
'''

# 분석에 활용할 열(특성)을 선택
ndf = rdf[['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'embarked']]
ndf.head()

# KNN 모델을 적용하려면 데이터 전처리 → 학습 데이터 준비 → 모델 학습 → 예측 및 평가의 단계를 거쳐야.
# 원핫인코딩 - 범주형 데이터를 모형이 인식할 수 있도록 숫자형으로 변환
onehot_sex = pd.get_dummies(ndf['sex']) #기존 sex열을 female, male 변환된 열을 추가
ndf = pd.concat([ndf, onehot_sex], axis=1)

onehot_embarked = pd.get_dummies(ndf['embarked'], prefix='town') #embarked 열을 town으로 시작하는 값으로 열추가
ndf = pd.concat([ndf, onehot_embarked], axis=1)

ndf = ndf.drop(['sex', 'embarked'], axis=1)
ndf.head()

'''
[Step 5] 데이터셋 구분 - 훈련용(train data)/ 검증용(test data)
'''
#KNN 모델을 학습하기 전에 데이터를 정규화(Normalization)하고, 학습(train)과 테스트(test) 데이터로 분할하는 과정
# 속성(변수) 선택
X = ndf[['pclass', 'age', 'sibsp', 'parch', 'female', 'male',
       'town_C', 'town_Q', 'town_S']]    #독립 변수 X
y = ndf['survived']                      #종속 변수 Y survived 열 → 모델이 예측해야 하는 목표 변수

# 설명 변수 데이터를 정규화(normalization)각 변수의 값이 서로 다른 단위와 범위를 가질 때,
# 이를 동일한 기준으로 맞추는 과정 KNN과 같은 거리 기반 알고리즘은 변수의 크기에 영향을 받으므로 정규화가 필수
from sklearn import preprocessing
# StandardScaler()평균을 0,표준편차를 1로 변환(표준 정규 분포로 변환)데이터의 스케일을 맞춰 특정변수의 영향력을 줄임
# 정규화를 하면 나이(age)와 같은 큰 숫자가 다른 변수(pclass 등)에 비해 모델에 영향을 주지 않도록 조정 가능
X = preprocessing.StandardScaler().fit(X).transform(X)

# train data 와 test data로 구분(7:3 비율)
from sklearn.model_selection import train_test_split
# 데이터를 학습용(Train) 70% / 테스트용(Test) 30% 비율로 분할
# random_state=10 실행할 때마다 같은 결과가 나오도록 랜덤 시드를 고정
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)
# 학습 데이터는 모델 훈련에 사용하고, 테스트 데이터는 성능 평가에 사용
print('train data 개수: ', X_train.shape)
print('test data 개수: ', X_test.shape)
train data 개수:  (474, 9)
test data 개수:  (204, 9)
'''
[Step 6] KNN 분류 모형 - sklearn 사용
'''
# 모델을 학습하고, 테스트 데이터를 예측하는 과정, 생존 여부(survived)를 예측하고 실제값(y_test)과 비교하는 단계
# 거리 기반 알고리즘이므로 데이터 정규화(StandardScaler)를 먼저 진행해야 한다.
# sklearn 라이브러리에서 KNN 분류 모형 가져오기
from sklearn.neighbors import KNeighborsClassifier

# 모형 객체 생성 (k=5로 설정) 보통 k=3~7 사이의 값을 선택하며, 적절한 K 값은 실험적으로 찾아야 함
knn = KNeighborsClassifier(n_neighbors=5)

# train data를 가지고 모형 학습
knn.fit(X_train, y_train)  

# test data를 가지고 y_hat을 예측 (분류) 학습한 KNN 모델을 사용하여 테스트 데이터(X_test)를 예측
y_hat = knn.predict(X_test)   # y_hat → KNN 모델이 예측한 생존 여부

print(y_hat[0:10])
print(y_test.values[0:10])   #예측값(y_hat)과 실제값(y_test)이 얼마나 일치하는지 확인
# 예측값과 실제값을 비교하여 모델의 성능을 평가할 수 있음
[0 1 0 1 0 0 1 1 1 1]
[1 1 0 1 0 1 1 1 1 0]
# 일치하는 개수: 7개 (위치 1, 3, 5, 6, 7, 8), 틀린 개수: 3개 (위치 0, 4, 9)
accuracy = (7 / 10) * 100
print(f'모델 정확도: {accuracy:.2f}%') # 모델이 70%의 정확도로 생존 여부를 예측함
모델 정확도: 70.00%
'''
[Step 6-1] KNN 분류 모형 - sklearn 사용
'''
# 모델을 학습하고, 테스트 데이터를 예측하는 과정, 생존 여부(survived)를 예측하고 실제값(y_test)과 비교하는 단계
# 거리 기반 알고리즘이므로 데이터 정규화(StandardScaler)를 먼저 진행해야 한다.
# sklearn 라이브러리에서 KNN 분류 모형 가져오기
from sklearn.neighbors import KNeighborsClassifier

# 모형 객체 생성 (k=7로 설정) 보통 k=3~7 사이의 값을 선택하며, 적절한 K 값은 실험적으로 찾아야 함
knn = KNeighborsClassifier(n_neighbors=7)   #7값적용

# train data를 가지고 모형 학습
knn.fit(X_train, y_train)  

# test data를 가지고 y_hat을 예측 (분류) 학습한 KNN 모델을 사용하여 테스트 데이터(X_test)를 예측
y_hat = knn.predict(X_test)   # y_hat → KNN 모델이 예측한 생존 여부

print(y_hat[0:10])
print(y_test.values[0:10])   #예측값(y_hat)과 실제값(y_test)이 얼마나 일치하는지 확인
# 예측값과 실제값을 비교하여 모델의 성능을 평가할 수 있음
[0 1 0 1 0 0 1 1 1 1]
[1 1 0 1 0 1 1 1 1 0]
# 일치하는 개수: 8개, 틀린 개수: 2개 (위치 5, 9)
accuracy = (8 / 10) * 100
print(f'모델 정확도: {accuracy:.2f}%') # 모델이 80%의 정확도로 생존 여부를 예측함
모델 정확도: 80.00%
'''
[Step 6-2] KNN 분류 모형 - sklearn 사용
'''
# 모델을 학습하고, 테스트 데이터를 예측하는 과정, 생존 여부(survived)를 예측하고 실제값(y_test)과 비교하는 단계
# 거리 기반 알고리즘이므로 데이터 정규화(StandardScaler)를 먼저 진행해야 한다.
# sklearn 라이브러리에서 KNN 분류 모형 가져오기
from sklearn.neighbors import KNeighborsClassifier

# 모형 객체 생성 (k=3로 설정) 보통 k=3~7 사이의 값을 선택하며, 적절한 K 값은 실험적으로 찾아야 함
knn = KNeighborsClassifier(n_neighbors=3)   #3값적용

# train data를 가지고 모형 학습
knn.fit(X_train, y_train)  

# test data를 가지고 y_hat을 예측 (분류) 학습한 KNN 모델을 사용하여 테스트 데이터(X_test)를 예측
y_hat = knn.predict(X_test)   # y_hat → KNN 모델이 예측한 생존 여부

print(y_hat[0:10])
print(y_test.values[0:10])   #예측값(y_hat)과 실제값(y_test)이 얼마나 일치하는지 확인
# 예측값과 실제값을 비교하여 모델의 성능을 평가할 수 있음
[0 1 0 1 0 0 1 1 1 1]
[1 1 0 1 0 1 1 1 1 0]
# 일치하는 개수: 7개, 틀린 개수: 3개 (위치 0, 5, 9)
accuracy = (7 / 10) * 100
print(f'모델 정확도: {accuracy:.2f}%') # 모델이 80%의 정확도로 생존 여부를 예측함
모델 정확도: 70.00%

# k 를 7로 했을때 80%

 

K 값을 5와 7로 설정했을 때, 모델의 정확도를 비교

from sklearn.metrics import accuracy_score

# KNN 모델 학습 및 예측 (k=5)
knn_5 = KNeighborsClassifier(n_neighbors=5)
knn_5.fit(X_train, y_train)
y_hat_5 = knn_5.predict(X_test)
accuracy_5 = accuracy_score(y_test, y_hat_5)

# KNN 모델 학습 및 예측 (k=7)
knn_7 = KNeighborsClassifier(n_neighbors=7)
knn_7.fit(X_train, y_train)
y_hat_7 = knn_7.predict(X_test)
accuracy_7 = accuracy_score(y_test, y_hat_7)

# 결과 출력
print(f'K=5일 때 정확도: {accuracy_5:.4f}')
print(f'K=7일 때 정확도: {accuracy_7:.4f}')
K=5일 때 정확도: 0.7549
K=7일 때 정확도: 0.7696

최적의 K 찾기 - 여러 개의 K를 실험

K=1부터 20까지 여러 개의 값을 실험하여 최적의 K를 찾을 수도 있음.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

# 한글 폰트 설정 (Windows 기준 나눔고딕, Mac은 AppleGothic 사용)
plt.rc('font', family='Malgun Gothic')  # Windows
# plt.rc('font', family='AppleGothic')  # Mac 사용자는 이 줄을 활성화
plt.rcParams['axes.unicode_minus'] = False  # 한글 폰트 사용 시 '-' 기호 깨지는 문제 해결

k_values = range(1, 21)
accuracies = []

for k in k_values:
    knn = KNeighborsClassifier(n_neighbors=k)
    knn.fit(X_train, y_train)
    y_hat = knn.predict(X_test)
    accuracies.append(accuracy_score(y_test, y_hat))

# 그래프 출력
plt.plot(k_values, accuracies, marker='o')
plt.xlabel('K 값')
plt.ylabel('정확도')
plt.title('K 값에 따른 정확도 변화')
plt.xticks(np.arange(1, 21, 1))
plt.show()

K값이 14일때 정확도가 가장높다

# 모형 성능 평가 - 평가지표 계산
knn_report = metrics.classification_report(y_test, y_hat)          
print(knn_report)
              precision    recall  f1-score   support

           0       0.79      0.86      0.82       123
           1       0.76      0.65      0.70        81

    accuracy                           0.78       204
   macro avg       0.77      0.76      0.76       204
weighted avg       0.78      0.78      0.78       204
  1. 전체적인 정확도는 78%로 준수하지만, 1 클래스(생존자)에 대한 예측 성능이 다소 낮음.
  2. 1 클래스(생존자)의 Recall이 0.65로 낮기 때문에, 생존자를 놓칠 가능성이 있음.
  3. 데이터 불균형 문제를 해결하기 위해 샘플링 기법(SMOTE)이나 가중치를 조정하는 방법을 고려할 수 있음.
  4. K 값을 변경하면서 성능이 더 좋아지는지 실험해볼 필요가 있음.

    K=14에서 가장 높은 정확도를 보이지만, 1 클래스(생존자)의 성능을 조금 더 높이려면 다른 방법(K 조정, 데이터 보정 등)을 고려하는 것이 좋음!

K=7로 했을때와 비교하면 

  • K=7에서는 정확도가 76%로 K=14(78%)보다 낮았음.
  • 1 클래스(생존자)의 Recall이 낮아, 생존자를 놓칠 가능성이 큼.
  • K=14가 더 높은 정확도를 보였으므로, K=14를 최적의 K로 선택하는 것이 더 적절함.

결론: K=14가 K=7보다 성능이 더 좋으므로, K=14를 선택하는 것이 더 적절함!

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)
# 데이터 정규화 방법을 StandardScaler() 대신 MinMaxScaler()로 변경하여 KNN 모델의 성능을 테스트하는 방법
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

# MinMaxScaler를 사용하여 데이터 정규화 (0~1 범위로 변환)
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)  # 정규화된 데이터 생성

# train data와 test data로 분할 (7:3 비율)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.3, random_state=10)
# MinMaxScaler()는 모든 데이터를 0과 1 사이로 정규화하여 KNN 모델의 거리 계산을 더 효과적으로 수행하도록 함
# KNN 모델 생성 (K=5 설정)
knn = KNeighborsClassifier(n_neighbors=5)

# 학습 데이터로 모델 훈련
knn.fit(X_train, y_train)

# 테스트 데이터를 사용하여 예측 수행
y_hat = knn.predict(X_test)
# 예측값과 실제값 비교 (앞 10개 출력)
print("예측값:", y_hat[:10])
print("실제값:", y_test.values[:10])

# 정확도(Accuracy) 계산
accuracy = accuracy_score(y_test, y_hat)
print(f'KNN 모델 (MinMaxScaler 적용) 정확도: {accuracy:.4f}')
예측값: [0 1 0 1 0 0 1 1 1 1]
실제값: [1 1 0 1 0 1 1 1 1 0]
KNN 모델 (MinMaxScaler 적용) 정확도: 0.7451
# KNN 모델 생성 (K=7 설정)
knn = KNeighborsClassifier(n_neighbors=7)

# 학습 데이터로 모델 훈련
knn.fit(X_train, y_train)

# 테스트 데이터를 사용하여 예측 수행
y_hat = knn.predict(X_test)
print("예측값:", y_hat[:10])
print("실제값:", y_test.values[:10])

# 정확도(Accuracy) 계산
accuracy = accuracy_score(y_test, y_hat)
print(f'KNN 모델 (MinMaxScaler 적용) 정확도: {accuracy:.4f}')
예측값: [0 1 0 1 0 0 1 1 1 1]
실제값: [1 1 0 1 0 1 1 1 1 0]
KNN 모델 (MinMaxScaler 적용) 정확도: 0.7598
# KNN 모델 생성 (K=3 설정)
knn = KNeighborsClassifier(n_neighbors=3)

# 학습 데이터로 모델 훈련
knn.fit(X_train, y_train)

# 테스트 데이터를 사용하여 예측 수행
y_hat = knn.predict(X_test)
print("예측값:", y_hat[:10])
print("실제값:", y_test.values[:10])

# 정확도(Accuracy) 계산
accuracy = accuracy_score(y_test, y_hat)
print(f'KNN 모델 (MinMaxScaler 적용) 정확도: {accuracy:.4f}')
예측값: [1 1 0 1 0 0 1 1 1 1]
실제값: [1 1 0 1 0 1 1 1 1 0]
KNN 모델 (MinMaxScaler 적용) 정확도: 0.7500

# k=7로 설정시 정확도 높음

# 모형 성능 평가 - Confusion Matrix 계산
from sklearn import metrics
knn_matrix = metrics.confusion_matrix(y_test, y_hat)  
print(knn_matrix)   # k=7로 설정한 값으로 함
[[103  20]
 [ 29  52]]
# Confusion Matrix 시각화
plt.figure(figsize=(8, 6))
sns.heatmap(knn_matrix, annot=True, fmt='d', cmap='Blues',
            xticklabels=['Negative', 'Positive'],
            yticklabels=['Negative', 'Positive'])

plt.title('Confusion Matrix')
plt.ylabel('Actual label')
plt.xlabel('Predicted label')
plt.show()

# 모형 성능 평가 - 평가지표 계산
knn_report = metrics.classification_report(y_test, y_hat)          
print(knn_report)
              precision    recall  f1-score   support

           0       0.78      0.84      0.81       123
           1       0.72      0.64      0.68        81

    accuracy                           0.76       204
   macro avg       0.75      0.74      0.74       204
weighted avg       0.76      0.76      0.76       204

# 성능 지표 값
#정확도 (Accuracy) 76%
#평균 정밀도 (macro avg precision) 75%
#평균 재현율 (macro avg recall) 74%
#평균 F1-score (macro avg f1-score) 74%

# 기본 라이브러리 불러오기  SVM분류 (Support Vector Machine, 서포트 벡터 머신)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

'''
[Step 1] 데이터 준비 - Seaborn에서 제공하는 titanic 데이터셋 가져오기
'''

# load_dataset 함수를 사용하여 데이터프레임으로 변환
df = sns.load_dataset('data/titanic')
'''
[Step 2 ~ 3] 데이터 전처리
'''

# 중복 데이터 제거
df = df.drop_duplicates()

# NaN값이 많은 deck 열을 삭제, embarked와 내용이 겹치는 embark_town 열을 삭제
rdf = df.drop(['deck', 'embark_town'], axis=1)  

# age 열에 나이 데이터가 없는 모든 행을 삭제 - NaN 값
rdf = rdf.dropna(subset=['age'], how='any', axis=0)  

# embarked 열의 NaN값을 승선도시 중에서 가장 많이 출현한 값으로 치환하기
most_freq = rdf['embarked'].mode()[0]    
rdf['embarked'] = rdf['embarked'].fillna(most_freq)
'''
[Step 4] 변수 선택
'''

# 분석에 활용할 열(속성)을 선택
ndf = rdf[['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'embarked']]

# 원핫인코딩 - 범주형 데이터를 모형이 인식할 수 있도록 숫자형으로 변환
onehot_sex = pd.get_dummies(ndf['sex'])
ndf = pd.concat([ndf, onehot_sex], axis=1)

onehot_embarked = pd.get_dummies(ndf['embarked'], prefix='town')
ndf = pd.concat([ndf, onehot_embarked], axis=1)

ndf = ndf.drop(['sex', 'embarked'], axis=1)
'''
[Step 5] 데이터셋 구분 - 훈련용(train data)/ 검증용(test data)
'''

# 속성(변수) 선택
X = ndf[['pclass', 'age', 'sibsp', 'parch', 'female', 'male',
       'town_C', 'town_Q', 'town_S']]  #독립 변수 X
y = ndf['survived']                      #종속 변수 Y

# 설명 변수 데이터를 정규화(normalization)
from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)

# train data 와 test data로 구분(7:3 비율)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)

print('train data 개수: ', X_train.shape)
print('test data 개수: ', X_test.shape)
train data 개수:  (474, 9)
test data 개수:  (204, 9)
'''
[Step 6] SVM 분류 모형 - sklearn 사용
'''

# sklearn 라이브러리에서 SVM 분류 모형 가져오기
from sklearn import svm

# 모형 객체 생성 (kernel='rbf' 적용)
svm_model = svm.SVC(kernel='rbf')

# train data를 가지고 모형 학습
svm_model.fit(X_train, y_train)  

# test data를 가지고 y_hat을 예측 (분류)
y_hat = svm_model.predict(X_test)

print(y_hat[0:10])
print(y_test.values[0:10])
[1 1 0 1 0 0 0 1 1 1]
[1 1 0 1 0 1 1 1 1 0]
# 모형 성능 평가 - Confusion Matrix 계산
from sklearn import metrics
svm_matrix = metrics.confusion_matrix(y_test, y_hat)  
print(svm_matrix)
[[104  19]
 [ 24  57]]
# Confusion Matrix 시각화

plt.figure(figsize=(8, 6))
sns.heatmap(svm_matrix, annot=True, fmt='d', cmap='OrRd',
            xticklabels=['Negative', 'Positive'],
            yticklabels=['Negative', 'Positive'])

plt.title('Confusion Matrix')
plt.ylabel('Actual label')
plt.xlabel('Predicted label')
plt.show()

# 모형 성능 평가 - 평가지표 계산
svm_report = metrics.classification_report(y_test, y_hat)            
print(svm_report)
              precision    recall  f1-score   support

           0       0.81      0.85      0.83       123
           1       0.75      0.70      0.73        81

    accuracy                           0.79       204
   macro avg       0.78      0.77      0.78       204
weighted avg       0.79      0.79      0.79       204
# 기본 라이브러리 불러오기   의사결정 나무
from sklearn import metrics
from sklearn import tree
from sklearn.model_selection import train_test_split
from sklearn import preprocessing

import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns

'''
[Step 1] 데이터 준비/ 기본 설정
'''

# Breast Cancer 데이터셋 가져오기 (출처: UCI ML Repository)
breast-cancer-wisconsin/breast-cancer-wisconsin.data'
df = pd.read_csv(uci_path, header=None)

# 열 이름 지정
df.columns = ['id', 'clump', 'cell_size', 'cell_shape', 'adhesion', 'epithlial',
              'bare_nuclei', 'chromatin', 'normal_nucleoli', 'mitoses', 'class']

# 데이터셋의 크기
print(df.shape)
(699, 11)
'''
[Step 2] 데이터 탐색
'''

# 데이터 살펴보기
df.sample(5)

# 데이터 자료형 확인
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 699 entries, 0 to 698
Data columns (total 11 columns):
 #   Column           Non-Null Count  Dtype 
---  ------           --------------  ----- 
 0   id               699 non-null    int64 
 1   clump            699 non-null    int64 
 2   cell_size        699 non-null    int64 
 3   cell_shape       699 non-null    int64 
 4   adhesion         699 non-null    int64 
 5   epithlial        699 non-null    int64 
 6   bare_nuclei      699 non-null    object
 7   chromatin        699 non-null    int64 
 8   normal_nucleoli  699 non-null    int64 
 9   mitoses          699 non-null    int64 
 10  class            699 non-null    int64 
dtypes: int64(10), object(1)
memory usage: 60.2+ KB
# 데이터 통계 요약정보 확인
df.describe(include='all')  #include='all'사용하면 숫자형뿐만 아니라 object타입 열에 대한 통계 요약정보를 알수 있다

# 누락 데이터 확인
df.isnull().sum()   #모든 열에 누락치가 없다.
id                 0
clump              0
cell_size          0
cell_shape         0
adhesion           0
epithlial          0
bare_nuclei        0
chromatin          0
normal_nucleoli    0
mitoses            0
class              0
dtype: int64
# 중복 데이터 확인
df.duplicated().sum()
8
# 목표변수
df['class'].value_counts(normalize=True)
class
2    0.655222
4    0.344778
Name: proportion, dtype: float64
# 목표변수를 이진 변수로 변환 - 2: benign(양성), 4: malignant(악성)
df['class'] = df['class'].map({2:0, 4:1})
df['class'].value_counts(normalize=True)
class
0    0.655222
1    0.344778
Name: proportion, dtype: float64
# pandas hist 시각화
df.hist(figsize=(15, 12));  # hist() 데이터프레임에 포함된 모든 숫자형열에 대한 히스트그램을 그린다.

# seaborn pairplot 시각화
vis_cols = ['clump', 'cell_size', 'cell_shape', 'chromatin',  'class']
sns.pairplot(data=df[vis_cols], hue='class');

'''
[Step 3] 데이터 전처리
'''
# 중복 데이터 제거
print('중복 제거 이전: ', df.shape)
df = df.drop_duplicates()
print('중복 제거 이후: ', df.shape)
중복 제거 이전:  (699, 11)
중복 제거 이후:  (691, 11)
### bare_nuclei 열의 자료형 변경 (문자열 ->숫자)
# bare_nuclei 열의 고유값 확인
print('bare_nuclei 열의 고유값: ', df['bare_nuclei'].unique())

df['bare_nuclei'] = df['bare_nuclei'].replace('?', np.nan)    # '?'을 np.nan으로 변경
df = df.dropna(subset=['bare_nuclei'], axis=0)                # 누락데이터 행을 삭제
df['bare_nuclei'] = df['bare_nuclei'].astype('int')           # 문자열을 숫자형으로 변환

# 데이터 통계 요약정보 확인
df.describe()

'''
[Step 4] 데이터셋 구분 - 훈련용(train data)/ 검증용(test data)
'''

# 속성(변수) 선택
train_features = ['clump', 'cell_size', 'cell_shape', 'adhesion', 'epithlial',
                  'bare_nuclei', 'chromatin', 'normal_nucleoli', 'mitoses']
X = df[train_features]         # 설명 변수 X
y = df['class']                # 예측 변수 Y

# 설명 변수 데이터를 정규화
X = preprocessing.StandardScaler().fit(X).transform(X)

# train data 와 test data로 구분(7:3 비율)
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    test_size=0.3,
                                                    random_state=10)

print('train data 개수: ', X_train.shape)
print('test data 개수: ', X_test.shape)
train data 개수:  (472, 9)
test data 개수:  (203, 9)
'''
[Step 5] Decision Tree 분류 모형 - sklearn 사용
'''

# sklearn 라이브러리에서 Decision Tree 분류 모형 가져오기

# 모형 객체 생성 (criterion='entropy' 적용)
tree_model = tree.DecisionTreeClassifier(criterion='entropy', max_depth=5)

# train data를 가지고 모형 학습
tree_model.fit(X_train, y_train)

# test data를 가지고 y_hat을 예측 (분류)
y_hat = tree_model.predict(X_test)      

print(y_hat[0:10])
print(y_test.values[0:10])
[0 1 1 0 0 1 1 0 0 1]
[0 1 1 0 0 1 1 0 0 1]
# 모형 성능 평가 - Confusion Matrix 계산
tree_matrix = metrics.confusion_matrix(y_test, y_hat)
print(tree_matrix)    # TN 123개 FP는 7개, 악성을 양성으로 잘못분류한 FN은 2개, 악성을 정확 예측한 TP는 71개
[[123   7]
 [  2  71]]
# Confusion Matrix 시각화

plt.figure(figsize=(8, 6))
sns.heatmap(tree_matrix, annot=True, fmt='d', cmap='Greens',
            xticklabels=['Negative', 'Positive'],
            yticklabels=['Negative', 'Positive'])

plt.title('Confusion Matrix')
plt.ylabel('Actual label')
plt.xlabel('Predicted label')
plt.show()

# 모형 성능 평가 - 평가지표 계산
tree_report = metrics.classification_report(y_test, y_hat)
print(tree_report)
              precision    recall  f1-score   support

           0       0.98      0.95      0.96       130
           1       0.91      0.97      0.94        73

    accuracy                           0.96       203
   macro avg       0.95      0.96      0.95       203
weighted avg       0.96      0.96      0.96       203
# 특성 중요도 출력
features = pd.DataFrame(tree_model.feature_importances_,
                        index=train_features,
                        columns=['Importance'])
features = features.sort_values(by='Importance', ascending=False)
features

# 특성 중요도 시각화
plt.figure(figsize=(10, 6))
sns.barplot(x=features.Importance, y=features.index,
            hue=features.index, legend=False,
            palette='viridis')
plt.title('Feature Importances')
plt.xlabel('Importance')
plt.ylabel('Features')
plt.show()