import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import requests
from bs4 import BeautifulSoup
import re
# 1. 크롤링 함수 정의 (사이트에서 체고 범위 긁어오기)
def get_dog_size(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# 사이트 구조 분석 결과: '체고' 텍스트가 포함된 li 태그 등을 찾아야 함
# 네가 준 사이트 텍스트 패턴: "체고 :: 수 50 ~ 55 Cm"
# 페이지 전체 텍스트에서 '체고' 관련 줄을 찾음
text_content = soup.get_text()
# 정규표현식으로 '수 00 ~ 00' 숫자 추출
# (사이트마다 형식이 조금 다를 수 있어서 유연하게 숫자만 뽑는 로직)
match = re.search(r'체고\\s*::\\s*수\\s*(\\d+)\\s*~\\s*(\\d+)', text_content)
if match:
min_h = int(match.group(1))
max_h = int(match.group(2))
avg_h = (min_h + max_h) / 2
return avg_h, min_h, max_h
else:
# 못 찾으면 기본값 리턴 (에러 방지)
return 0, 0, 0
# ==========================================
# 1. 데이터 생성 (Data Generation)
# ==========================================
# 2. 실제 크롤링 실행
jindo_url = "<https://www.nias.go.kr/companion/dog_stage.do?cmCode=M170718155128718&gubun=25>"
dach_url = "<https://www.nias.go.kr/companion/dog_stage.do?cmCode=M170718155128718&gubun=5>"
# 진돗개 정보 긁기
j_avg, j_min, j_max = get_dog_size(jindo_url)
print(f"크롤링 된 진돗개 수컷 체고: 평균 {j_avg}cm (범위 {j_min}~{j_max})")
# 닥스훈트 정보 긁기
d_avg, d_min, d_max = get_dog_size(dach_url)
print(f"크롤링 된 닥스훈트 수컷 체고: 평균 {d_avg}cm (범위 {d_min}~{d_max})")
# 3. 긁어온 정보로 데이터 200개 '생성' (Simulation)
np.random.seed(42)
# 진돗개 생성 (긁어온 j_avg 사용)
# 표준편차는 대략 범위의 1/4 정도로 설정하면 자연스러움
j_height = np.random.normal(j_avg, (j_max - j_min)/4, 200)
j_length = j_height * 1.05 + np.random.normal(0, 2, 200) # 비율 1:1.05 반영
j_data = np.column_stack((j_length, j_height))
j_label = np.ones(200)
# 닥스훈트 생성 (긁어온 d_avg 사용)
d_height = np.random.normal(d_avg, (d_max - d_min)/4, 200)
d_length = d_height * 2.0 + np.random.normal(0, 2, 200) # 비율 1:2.0 반영
d_data = np.column_stack((d_length, d_height))
d_label = np.zeros(200)
print("\\n데이터 생성 완료!")
print(f"진돗개 샘플: 키 {j_height[0]:.1f}, 길이 {j_length[0]:.1f}")
print(f"닥스훈트 샘플: 키 {d_height[0]:.1f}, 길이 {d_length[0]:.1f}")
# 데이터 합치기
X = np.concatenate((d_data, j_data)) # 특성 (길이, 키)
y = np.concatenate((d_label, j_label)) # 정답 (0, 1)
# ==========================================
# 2. 데이터 분리 (Train : Test = 8 : 2)
# ==========================================
# shuffle=True가 기본값이라서 데이터가 자동으로 섞임
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"전체 데이터 수: {len(X)}")
print(f"학습용 데이터(Train): {len(X_train)}")
print(f"테스트용 데이터(Test): {len(X_test)}")
print("-" * 30)
# ==========================================
# 3. 모델 학습 및 평가 1: K-NN
# ==========================================
k = 3
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train, y_train)
y_pred_knn = knn.predict(X_test)
acc_knn = accuracy_score(y_test, y_pred_knn)
print(f"K-NN (k={k}) 정확도: {acc_knn * 100:.2f}%")
# ==========================================
# 4. 모델 학습 및 평가 2: SVM (커널 함수)
# ==========================================
# SVM은 커널(Kernel)함수를 이용해 데이터를 고차원으로 보내서 분류함
# kernel='linear' (선형), 'rbf' (곡선, 기본값)
svm_model = SVC(kernel='rbf', C=1.0, gamma='scale')
svm_model.fit(X_train, y_train)
y_pred_svm = svm_model.predict(X_test)
acc_svm = accuracy_score(y_test, y_pred_svm)
print(f"SVM (Kernel=RBF) 정확도: {acc_svm * 100:.2f}%")
# ==========================================
# 5. 시각화 (Visualization)
# ==========================================
plt.figure(figsize=(10, 6))
# 닥스훈트 데이터 (Label 0)
plt.scatter(d_length, d_height, c='red', alpha=0.5, label='Dachshund')
# 진돗개 데이터 (Label 1)
plt.scatter(j_length, j_height, c='blue', alpha=0.5, label='Jindo Dog')
# 테스트용 데이터 중 하나를 찍어서 확인해보기 (예시)
test_sample = X_test[0]
plt.scatter(test_sample[0], test_sample[1], s=200, c='green', marker='*', label='Test Sample')
plt.xlabel('Length (cm)')
plt.ylabel('Height (cm)')
plt.title('Dachshund vs Jindo Dog Distribution (Generated Data)')
plt.legend()
plt.grid(True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
# (이전에 만든 데이터 X_train, y_train이 있다고 가정)
# 만약 없다면 위에서 짠 "데이터 생성 + 분리" 코드를 먼저 실행해야 함
# 1. K-NN 모델 학습 (K=3)
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
# 2. 새로운 데이터 정의 (숙제: [59, 35])
new_data = [[59, 35]]
# 3. [핵심] 이웃 찾기
# distances: 거리, indexes: 이웃의 인덱스 번호
distances, indexes = knn.kneighbors(new_data)
# 이웃 데이터만 따로 추출
neighbor_data = X_train[indexes]
# 4. 시각화
plt.figure(figsize=(10, 6))
# (1) 전체 데이터 (배경)
# 닥스훈트(0)는 빨간색, 진돗개(1)는 파란색
plt.scatter(X_train[y_train==0][:,0], X_train[y_train==0][:,1], c='red', alpha=0.3, label='Dachshund')
plt.scatter(X_train[y_train==1][:,0], X_train[y_train==1][:,1], c='blue', alpha=0.3, label='Jindo Dog')
# (2) [숙제 요구사항] 이웃한 3개의 데이터 -> 주황색
# neighbor_data는 3차원 배열([1, 3, 2])로 나와서 [0]인덱스로 접근해야 함
plt.scatter(neighbor_data[0][:,0], neighbor_data[0][:,1],
s=150, c='orange', marker='o', label='Neighbors (Orange)')
# (3) [숙제 요구사항] 새로운 데이터 -> 초록색 세모
plt.scatter(new_data[0][0], new_data[0][1],
s=200, c='green', marker='^', label='New Data (Green Triangle)')
plt.xlabel('Length (cm)')
plt.ylabel('Height (cm)')
plt.title('K-NN Neighbor Visualization (Homework)')
plt.legend()
plt.grid(True)
plt.show()
# 결과 출력
print("이웃 인덱스:", indexes)
from sklearn.svm import SVC
# 1. SVM 모델 학습 (Linear 커널 사용 시 직선)
svm = SVC(kernel='linear', C=1.0)
svm.fit(X_train, y_train)
# 2. 시각화 준비
plt.figure(figsize=(10, 6))
# 데이터 뿌리기
plt.scatter(X_train[y_train==0][:,0], X_train[y_train==0][:,1], c='red', alpha=0.3)
plt.scatter(X_train[y_train==1][:,0], X_train[y_train==1][:,1], c='blue', alpha=0.3)
plt.scatter(new_data[0][0], new_data[0][1], s=200, c='green', marker='^', label='New Data')
# 3. 결정 경계(선) 그리기
# 현재 축의 범위 가져오기
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# 그리드 생성하여 등고선(Contour) 그리기
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = svm.decision_function(xy).reshape(XX.shape)
# 경계선과 마진(Margin) 그리기
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
plt.title('SVM Decision Boundary (Not Neighbors)')
plt.show()