데이터사이언스/추천시스템

맥주 추천시스템 구현 - 4. 클러스터링

ghtis1798 2021. 2. 26. 01:16

클러스터링

📊맥주별 클러스터링 시각화

이번엔 맥주 데이터를 5가지 평가 요소를 기준으로 클러스터링 해보았습니다.

향, 외관, 맛 등의 요소에 따라 서로 다른 군집들이 나타날 것 같았기 때문입니다.

우선 필요한 라이브러리들을 import하고 데이터도 불러오겠습니다.

시각화 라이브러리로는 📊plotly를 사용했습니다.

import pandas as pd

import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as po

from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler, MinMaxScaler

import warnings

warnings.filterwarnings('ignore')
pd.set_option('display.max_rows', 100)

data = pd.read_csv('전처리후데이터.csv', encoding='utf-8', index_col=0)
data.head(3)

✅맥주별 평점 계산하기

하나의 맥주에 대한 수많은 평가들이 있습니다.

이것을 맥주별 평점으로 환산하겠습니다.

우선 맥주 종류들을 뽑아내고 5가지 요소를 추가하려고 합니다.

tmp = data.copy()
tmp = tmp[['맥주']]

# 맥주 이름 중복행 제거 : df.drop_duplicates()
tmp.drop_duplicates(keep='first', inplace=True)
cols = ['Aroma','Appearance','Flavor','Mouthfeel','Overall']

# 5가지 요소를 컬럼에 추가합니다.
for col in cols:
    tmp[col] = ''
tmp.head(5)

데이터 프레임은 잘 형성되었습니다.

이제 요소별 평균 평점을 계산하겠습니다.

# 전체 맥주 개수만큼 실행
for i in range(len(tmp)):
    # 맥주 이름 추출
    beer = tmp['맥주'].iloc[i]

    # 평가한 유저 수
    length = len(data[data['맥주']==beer])
    # 5가지 평가 요소 계산
    for col in cols:
        # 요소별 평점 합
        col_sum = data[data['맥주']==beer][col].sum()
        # 요소별 평균 구하기
        tmp[col].iloc[i] = col_sum/length

tmp.to_csv('맥주_종류별_평점_77.csv', encoding='utf-8')
tmp.head()

5가지 요소에 대한 평점이 잘 계산되었습니다.

클러스터링

실수형 컬럼들만 뽑아낸 뒤 표준화시켰습니다.

beer_names = data[['맥주']]

beer_values = data[['Aroma','Appearance','Flavor','Mouthfeel','Overall']]

# 값 표준화
scaler = MinMaxScaler()

scaler.fit(beer_values)

scaled = scaler.transform(beer_values)
scaled

이를 바탕으로 elbow point를 찾아보겠습니다.

def elbow(X):
    sse = []

    for i in range(1,11):
        km = KMeans(n_clusters=i)
        km.fit(X)
        sse.append(km.inertia_)

    fig = px.line(sse)
    fig.show()

elbow(scaled)

x축은 cluster의 수, y축은 클러스터 내 요소들 간 거리를 제곱한 값입니다.

cluster 수는 3개로 정했습니다.

km = KMeans(n_clusters=3).fit(scaled)
km.cluster_centers_

예측된 결과값도 저장해줍니다.

# 클러스터링을 통해 맥주별 군집 예측
predict = pd.DataFrame(km.predict(scaled))
predict.columns = ['Cluster']
predict.head(3)

그리고 다시 데이터프레임으로 저장합니다.

scaled = pd.DataFrame(data=scaled, columns=['Aroma', 'Appearance', 'Flavor', 'Mouthfeel','Overall'])
scaled

여기서 beer_names의 경우 index가 0부터 순차 정렬되어 있지 않습니다.

따라서 나온 결과값과 병합하기 위해 index를 정렬합니다.

# 이거 맞춰줘야함
beer_names.reset_index(inplace=True, drop=True)

# scale 값과 클러스터링 예측값, 기존 맥주이름, 편의점 데이터 병합
result = pd.concat([scaled, beer_names], axis=1).reset_index(drop=True)
result

클러스터링 예측값도 합쳐주겠습니다.

# scale 값과 클러스터링 예측값, 기존 맥주이름, 편의점 데이터 병합
result = pd.concat([result, predict], axis=1).reset_index(drop=True)
result

그리고 각 클러스터 군집들의 Aroma, Appearance, Flavor, Mouthfeel, Overall은 어떤지 살펴보겠습니다.

c_result = km.cluster_centers_
c_result = pd.DataFrame(data=c_result)
c_result.columns = ['Aroma', 'Appearance', 'Flavor','Mouthfeel','Overall']
c_result.sort_values(by='Overall', inplace=True)
c_result

클러스터링 결과를 저장합니다.

c_result.to_csv('대표군집클러스터링.csv', encoding='utf-8')
result.to_csv('전체맥주클러스터링.csv', encoding='utf-8')

✅클러스터링 시각화

마지막으로는 각 클러스터와 특정 맥주의 군집을 방사형 차트로 표현해보겠습니다.

def show_cluster(result, name):
    categories = ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall']
    color = ['skyblue', 'blue', 'salmon', 'green']

    target = result[result['맥주'] == name]
    cluster = target['Cluster'].iloc[0]
    target = target[categories]

    fig = go.Figure()

    fig.add_trace(go.Scatterpolar(
        r = c_result.values[0],
        theta = categories,
        fill='toself',
        name='Soso',
        line_color=color[0]
    ))

    fig.add_trace(go.Scatterpolar(
        r = c_result.values[1],
        theta = categories,
        fill='toself',
        name='Sad',
        line_color=color[1]
    ))

    fig.add_trace(go.Scatterpolar(
        r = c_result.values[2],
        theta = categories,
        fill='toself',
        name='Good',
        line_color=color[2]
    ))

    fig.add_trace(go.Scatterpolar(
        r = target.values[0],
        theta = categories,
        fill='toself',
        name=name,
        line_color=color[3]
    ))

    fig.update_layout(
      polar=dict(
        radialaxis=dict(
          visible=True,
        )),
    )

    fig.show()

# 클라우드 맥주 클러스터링 결과
show_cluster(result, 'Kloud Original Gravity')

# 코젤 다크의 클러스터링 결과
show_cluster(result, 'Kozel Černý (Dark) 10°')

5가지 요소에 대해 특별한 인사이트를 찾을 수 없었다는 점이 아쉽습니다.

서로 모양도 비슷합니다.

다만 해당 맥주의 보편적인 평가는 유추할 수 있을 것 같습니다.

따라서 Sad, Soso, Good으로 클러스터링 결과를 Labeling하였습니다.

해당 맥주의 특징을 결정짓는 요소들을 더 추가했다면 유의미한 결과가 나왔을지도 모르겠습니다. 😢

다음 포스팅에서는 직접 추천시스템을 구현해 보려고 합니다.