import pandas as pd
# 1. 데이터 준비
col_names = ['sepal-length', 'sepal-width',
'petal-length', 'petal-width',
'Class']
# csv 파일에서 DataFrame을 생성
dataset = pd.read_csv('iris.csv', encoding='UTF-8', header=None, names=col_names)
# DataFrame 확인
print(dataset.shape) # (row개수, column개수)
print(dataset.info()) # 데이터 타입, row 개수, column 개수, 컬럼 데이터 타입
print(dataset.describe()) # 요약 통계 정보
(150, 5)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
sepal-length 150 non-null float64
sepal-width 150 non-null float64
petal-length 150 non-null float64
petal-width 150 non-null float64
Class 150 non-null object
dtypes: float64(4), object(1)
memory usage: 6.0+ KB
None
sepal-length sepal-width petal-length petal-width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.054000 3.758667 1.198667
std 0.828066 0.433594 1.764420 0.763161
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000
print(dataset.iloc[0:5]) # dataset.head()
print(dataset.iloc[-5:]) # dataset.tail()
sepal-length sepal-width petal-length petal-width Class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
sepal-length sepal-width petal-length petal-width Class
145 6.7 3.0 5.2 2.3 Iris-virginica
146 6.3 2.5 5.0 1.9 Iris-virginica
147 6.5 3.0 5.2 2.0 Iris-virginica
148 6.2 3.4 5.4 2.3 Iris-virginica
149 5.9 3.0 5.1 1.8 Iris-virginica
# 데이터 전처리:
# 데이터 세트를 데이터(포인트)와 레이블로 구분
# X = 전체 행, 마지막 열 제외한 모든 열 데이터 -> n차원 공간의 포인트
X = dataset.iloc[:,:-1].to_numpy() # DataFrame을 np.ndarray로 변환
print(X)
[[5.1 3.5 1.4 0.2]
[4.9 3. 1.4 0.2]
[4.7 3.2 1.3 0.2]
[4.6 3.1 1.5 0.2]
... >> Class 컬럼이 사라짐
# 전체 데이터 세트를 학습 세트(training set)와 검증 세트(test set)로 나눔
# y = 전체 행, 마지막 열 데이터
y = dataset.iloc[:, 4].to_numpy()
print(y)
['Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa'
'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa'
'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa'
'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa'
'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa' 'Iris-setosa'
... >> Class 컬럼만 분리
from sklearn.model_selection import train_test_split
# 전체 데이터 세트를 학습 세트(training set)와 검증 세트(test set)로 나눔
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
print(len(X_train), len(X_test))
120 30
print(X_train[:3])
print(y_train[:3])
[[5.1 3.5 1.4 0.2]
[5.1 3.7 1.5 0.4]
[7.3 2.9 6.3 1.8]]
['Iris-setosa' 'Iris-setosa' 'Iris-virginica']
# 3. 거리 계산을 위해서 각 특성들을 스케일링(표준화)
# Z-score 표준화: 평균을 0, 표준편차 1로 변환
from sklearn.preprocessing import StandardScaler
# 3. 거리 계산을 위해서 각 특성들을 스케일링(표준화)
# Z-score 표준화: 평균을 0, 표준편차 1로 변환
scaler = StandardScaler() # Scaler 객체 생성
scaler.fit(X_train) # 스케일링(표준화)를 위한 평균과 표준 편차 계산
X_train = scaler.transform(X_train) # 스케일링(표준화 수행)
X_test = scaler.transform(X_test)
# 스케일링(z-score 표준화 수행 결과 확인)
for col in range(4):
print(f'평균 = {X_train[:, col].mean()}, 표준편차= {X_train[:, col].std()}')
평균 = 2.174186756557598e-15, 표준편차= 1.0
평균 = 1.5802174383831394e-15, 표준편차= 1.0000000000000002
평균 = -4.0338103228047354e-16, 표준편차= 0.9999999999999999
평균 = -1.7763568394002506e-16, 표준편차= 1.0000000000000002
for col in range(4):
print(f'평균 = {X_test[:, col].mean()}, 표준편차= {X_test[:, col].std()}')
평균 = -0.1516498303233526, 표준편차= 0.8221796199510816
평균 = -0.14194351771768907, 표준편차= 1.1653424448123006
평균 = -0.06432049126899704, 표준편차= 0.913042651577681
평균 = -0.07729170479281523, 표준편차= 0.8153649149910354
>> train_set의 평균과 표준편차를 사용했기 때문에 평균이 0, 표준편차가 1이 아니게 된다.
# 4. 학습/예측(Training/Pradiction)
from sklearn.neighbors import KNeighborsClassifier
# k-NN 분류기를 생성
classifier = KNeighborsClassifier(n_neighbors=5)
# 분류기 학습
classifier.fit(X_train, y_train)
# 예측
y_pred= classifier.predict(X_test)
print(y_pred)
['Iris-versicolor' 'Iris-virginica' 'Iris-virginica' 'Iris-setosa'
'Iris-setosa' 'Iris-setosa' 'Iris-virginica' 'Iris-virginica'
'Iris-virginica' 'Iris-setosa' 'Iris-virginica' 'Iris-virginica'
'Iris-versicolor' 'Iris-virginica' 'Iris-versicolor' 'Iris-versicolor'
'Iris-versicolor' 'Iris-setosa' 'Iris-virginica' 'Iris-setosa'
'Iris-virginica' 'Iris-versicolor' 'Iris-virginica' 'Iris-setosa'
'Iris-setosa' 'Iris-setosa' 'Iris-virginica' 'Iris-virginica'
'Iris-versicolor' 'Iris-setosa']
5. 모델 평가
from sklearn.metrics import confusion_matrix
conf_matrix= confusion_matrix(y_test, y_pred)
print(conf_matrix)
[[12 0 0]
[ 0 10 1]
[ 0 0 7]]
>> 대각선에 있는 숫자가 정답을 맞춘 것, 그 외가 틀린 것
from sklearn.metrics import classification_report
report = classification_report(y_test, y_pred)
print(report)
precision recall f1-score support
Iris-setosa 1.00 1.00 1.00 12
Iris-versicolor 1.00 0.91 0.95 11
Iris-virginica 0.88 1.00 0.93 7
accuracy 0.97 30
macro avg 0.96 0.97 0.96 30
weighted avg 0.97 0.97 0.97 30
밑바닥부터 시작하는 데이터 과학 p147
# 정확도(accuracy) = (전체 정답 수) / (전체 문제 수)
# 민감도(sensitivity) = (맞춘 Positive 수) / (실제 Positive 수)
# 특이도(specificity) = (맞춘 Negative 수) / (실제 Negative 수)
# 정밀도(precision) = (맞춘 Positive 수) / (예측 Positive 수)
# 재현율(recall) = 민감도(sensitivity)
import numpy as np
# 6. 모델 개선 - k값을 변화시킬 때, 에러가 줄어드는 지
errors = []
for i in range(1, 31):
knn = KNeighborsClassifier(n_neighbors = i)
knn.fit(X_train, y_train)
pred_i = knn.predict(X_test)
errors.append(np.mean(pred_i != y_test))
print(errors)
[0.03333333333333333, 0.1, 0.03333333333333333, 0.03333333333333333, 0.03333333333333333, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.03333333333333333, 0.03333333333333333, 0.06666666666666667, 0.03333333333333333, 0.06666666666666667, 0.06666666666666667, 0.1, 0.06666666666666667, 0.1, 0.03333333333333333, 0.1, 0.03333333333333333, 0.1, 0.06666666666666667, 0.1, 0.03333333333333333]
>> 여기서 에러가 가장 적은 것을 선택
import matplotlib.pyplot as plt
plt.plot(range(1, 31), errors, marker='o')
plt.title('Mean error with K-Value')
plt.xlabel('k-value')
plt.ylabel('mean error')
plt.show()