본문 바로가기

Python

Python - 군집분석

군집분석

각 객체의 유사성을 측정하여 유사성이 높은 대상 집단을 분류하고, 군집에 속한 객체들의 유사성과 서로 다른 군집에 속한 객체 간의 상이성을 규명하는 분석 방법이다. 군집의 개수나 구조에 대한 가정 없이 데이터들의 사이의 거리를 기준으로 군집화를 유도한다. 

 

* 거리 측정 방법의 유형

가. 연속성 변수 

1. 유클리디안 거리 : 데이터간의 유사성을 측정할 때 많이 사용하는 거리. 통계적 개념이 내포되어 있지 않아 변수들의 산포 정도가 전혀 감안되어 있지 않았다.

2. 표준화 거리 : 해당변수의 표준편차로 척도 변환한 후 유클리디안 거리를 계산하는 방법이다. 표준화하면, 척도의 차이, 분산의 차이로 인한 왜곡을 피할 수 있다.

 

3. 맨하탄 거리 : 유클리디안 거리와 함께 많이 사용되는 거리로 맨하탄 도시에서 건물에서 건물을 가기 위한 최단 거리를 구하기 위해 고안된 거리이다.

4. 민코우스키 거리 : 맨하탄 거리와 유클리디안 거리를 한번에 표현한 공식으로 L1(맨하탄)거리, L2(유클리디안)거리라고 불리고 있다.

 

나. 범주형 변수

1. 자카드 거리

2. 코사인 거리

 

 

 

* 유형에 따라 크게 2가지로 분류가 가능하다.

가. 계층적 군집 : n개의 군집으로 시작하여, 점차 군집의 개수를 줄여 나가는 방법

1. 최단 연결법 : 거리가 가장 가까운 데이터를 묶어서 군집을 형성. 군집과 군집 또는 데이터와의 거리를 계산시 최단거리로 거리를 계산하여, 거리행렬을 수정한다. 

 

2. 최장 연결법 : 군집과 군집 또는 데이터와의 거리를 계산할 때 최장거리를 거리로 계산하여 거리행렬을 수정한다.

 

3. 평균 연결법 : 군집과 군집 또는 데이터와의 거리를 계산할 때 평균을 거리로 계사하여 거리행렬을 수정한다.

4. 와드 연결법 : 군집내 편차들의 제곱합을 고려한 방법. 군집 간 정보의 손실을 최소화하기 위해 군집화를 진행한다. 

 

나. 비계층적 군집 : n개의 개체를 g개의 군집으로 나눌 수 있는 모든 가능한 방법을 점검해 최적화한 군집을 형성하는 것이다. 

 

1. k-평균 군집분석 : k개의 클러스터로 묶는 알고리즘으로 각 클러스터의 거리 차이의 분산을 최소화하는 방식으로 동작한다. 

 

* k-평균 군집분석의 과정

a. 원하는 군집의 개수와 초기 값들을 정해 seed 중심으로 군집을 형성한다 

b. 각 데이터를 거리가 가장 가까운 seed가 있는 군집으로 분류한다.

c. 각 군집의 seed 값을 다시 계산한다.

 

 

 

'''
   군집(비지도학습) : DBSCAN 
          => 데이터가 위치에서 공간 밀집도를 기준으로 클러스터를 구분 
'''

import pandas as pd
import folium

file_path = "2016_middle_shcool_graduates_report.xlsx"
df = pd.read_excel(file_path, engine='openpyxl', header=0)
pd.set_option('display.width', None) # 출력 화면의 너비
pd.set_option('display.max_rows', 100) # 출력할 행의 개수 한도
pd.set_option('display.max_columns', 10) # 출력할 열의 개수 한도
pd.set_option('display.max_colwidth', 20) # 출력할 열의 너비 

# 열의 이름 조회하기
df.columns.values
df.head()
df.info()

# df 데이터에서 각 중학교의 정보를 지도로 표시하기
mschool_map = folium.Map(location=[37.55,126.98],zoom_start=12)

for name, lat, lng in zip(df.index, df.위도, df.경도):
    folium.CircleMarker([lat,lng], 
                  radius=5, 
                  color='brown', 
                  fill=True, 
                  fill_color='coral',
                  fill_opacity=0.7,
                  popup=name,
                  tooltip=name
     ).add_to(mschool_map)
    
mschool_map.save('middle.html')

'''
데이터 전처리
 지역, 유형, 주야열을 원핫인코딩으로 
'''
df.info()
df["코드"].unique()

from sklearn import preprocessing

# 문자열 -> 수치형. 숫자의 크기 정보는 의미없음. 단순하게 종류로 표시.
label_encoder = preprocessing.LabelEncoder() #Label encoder
onehot_encoder = preprocessing.OneHotEncoder() # one hot encoder

label_location = label_encoder.fit_transform(df["지역"])
label_code = label_encoder.fit_transform(df["코드"])
label_type = label_encoder.fit_transform(df["유형"])
label_day = label_encoder.fit_transform(df["주야"])

label_location
label_type

#label_encoder 된 데이터를 df에 추가하자
df["location"] = label_location
df["code"] = label_code
df["type"] = label_type
df["day"] = label_day

df["location"].unique()
df["code"].unique()

df.head()

# DBSCAN 군집 모형 - sklearn 사용
# sklearn 라이브러리에서 cluster 군진 모형 가져오기
from sklearn import cluster

# 분석에 사용할 속성을 선택(과학고, 외고국제고, 자사고 진학률)
columns_list = [9,10,13]
X = df.iloc[:, columns_list] #군집분석에 사용될 속성 선택.
print(X[:5])

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

# DBSCAN 모형 객체 생성
# eps = 반지름 
# min_samples = 5 : 클러스터의 포인트의 갯수가 최소 5개 존재해야한다.
dbm = cluster.DBSCAN(eps=0.2, min_samples=5)

# 모형 학습
dbm.fit(X)

# 예측 (군집)
cluster_label = dbm.labels_
print(cluster_label)

# 예측 결과를 데이터 프레임에 추가 
df["Cluster"] = cluster_label
df.head()
df.info()
df["Cluster"].unique()
# -1,0,1,2,3 4개의 클러스터 그룹화 
# -1 그룹 : 어느 클러스터에 속하지 못한 노이스 그룹


# 클러스터별로 그룹화하여 그룹별 내용 5개만 출력하기
# 조회되는 컬럼은 0,1,3,9,10,13만 조회함
# Cluster 컬럼으로 그룹화 
grouped = df.groupby("Cluster")
grouped

for key, group in grouped :
    print('* key:',key)
    print('*number :',len(group))
    print(group.iloc[:,[0,1,3,9,10,13]].head())
    print('\n')

# 클러스터별로 지도의 색으로 표시하기
# 그래프로 표현 - 시각화
colors = {-1:'gray',0:'coral',1:'blue',2:'green',
          3:'red',4:'purple',5:'orange',6:'brown',
          7:'brick',8:'yellow',9:'magenta',10:'cyan',
          11:'tan'}

cluster_map = folium.Map(location=[37.55,126.98],zoom_start=12)
for name,lat,lng,clus in \
              zip(df.학교명,df.위도,df.경도,df.Cluster):
    folium.CircleMarker([lat,lng],
                        radius=5,
                        color=colors[clus],
                        fill=True,
                        fill_color=colors[clus],
                        fill_opactiy=0.7,
                        popup=name,
                        tooltip=name
    ).add_to(cluster_map)
cluster_map.save('./seoul_mschool_cluster.html')
df.info()

# 설명변수 X2 
# 과학고, 외고국제고, 자사고 진학율 + 유형을 넣어서 클러스터 생성하기
# 클러스터별로 지도 작성하기 

columns_list2 = [9,10,13,22]
X2 = df.iloc[:, columns_list2] #군집분석에 사용될 속성 선택.
print(X2[:5])

# DBSCAN 모형 객체 생성
# eps = 반지름 
# min_samples = 5 : 클러스터의 포인트의 갯수가 최소 5개 존재해야한다.
X2 = preprocessing.StandardScaler().fit(X2).transform(X2)
dbm2 = cluster.DBSCAN(eps=0.2, min_samples=5)
dbm2.fit(X2)

df['Cluster2'] = dbm2.labels_

grouped2_cols = [0,1,3] + columns_list2
grouped2_cols
grouped2 = df.groupby('Cluster2')

for key, group in grouped2 :
    print('* key:',key)
    print('*number :',len(group))
    print(group.iloc[:,grouped2_cols].head())
    print('\n')

cluster2_map = folium.Map(location=[37.55, 126.98],
                          zoom_start=12)

for name,lat,lng,clus in zip(df.학교명,df.위도,df.경도,df.Cluster2):
    folium.CircleMarker([lat,lng],
                        radius=5,
                        color=colors[clus],
                        fill=True,
                        fill_color=colors[clus],
                        fill_opactiy=0.7,
                        popup=name,
                        tooltip=name
    ).add_to(cluster2_map)
cluster_map.save('./seoul_mschool_cluster2.html')
df.info()