세조목

최종 프로젝트 21일차(24.04.18) 본문

데이터 분석 공부/프로젝트

최종 프로젝트 21일차(24.04.18)

세조목 2024. 4. 18. 21:41

최종 프로젝트 20일차입니다.

전일 클러스터링 분석을 위한 기초 작업을 모두 마쳐놓았습니다.

 

금일 진행했던 사항은 두 가지입니다.

  1. 군집 개수 정하기
  2. 컬럼 제거하면서 군집 결과 확인하기

군집 개수 정하기

우선 군집 개수를 먼저 정해보았습니다.

elbow plot과 실루엣 계수를 기준으로 판단을 내렸는데요,

어제 진행했던 표준화 작업을 금일 두 가지 버전으로 다시 한 번 진행했습니다.

하나는 Review_counts, Review_score, slope만 진행하는 방식이고,

나머지 하나는 sentiment를 제외한 모든 컬럼을 진행하는 방식입니다.

sentiment의 경우 어짜피 0과 1 둘 중 하나의 값으로만 이루어져있기 때문에

표준화를 진행할 필요가 없었는데요,

Review_counts, Review_score, slope 컬럼의 경우 다른 특성 컬럼들에 비해 값의

크기가 큰 경우가 많았기 때문에 해당 컬럼들만을 가지고서 표준화를 진행해보았습니다.

 

첫번째 경우

두번째 경우

Ver.4가 첫번째 경우이고, Ver.5가 두번째 경우입니다.

결과부터 얘기하면 저희는 Ver.5(모든 컬럼 표준화)를 선택했는데요,

특성 컬럼 값의 경우도 -100부터 100까지 분포하고 있기 때문에

Review_score, Review_counts, slope만 표준화 했을 때 마찬가지로

값의 차이가 많이 날 것으로 봤기 때문입니다.

 

컬럼 제거하면서 군집 결과 확인하기

Ver.5로 elbow plot을 그렸을 때 최적의 K값은 19였습니다.

물론 이 값 역시도 절대적이지는 않지만 하나의 기준은 잡아야 시작할 수 있기때문에

일단 K값 19부터 시작했습니다.

 

그런 다음 저희는 컬럼을 넣었다 뺐다 하면서 군집이 잘 이루어졌는지를

판단할 것이기 때문에 어떤 컬럼을 우선적으로 삭제할 것인지 먼저 정했습니다.

저희가 정한 기준은 다음과 같습니다.

  1. 상관관계가 높은 컬럼
  2. 팀의 주관적 판단
  3. 상관관계  heatmap의 한 행을 봤을 때 전체적으로 색이 옅은(=대부분 컬럼들과의 상관관계가 낮은) 컬럼

우선 1번부터 진행하기로 했습니다.

컬럼의 개수가 29개로 하나 하나 넣었다 뺐다 할 때 경우의 수가 엄청 많기 때문에

팀원들끼리 분량을 나눠서 진행했는데요,

 

일단 상관계수가 0.1 이상인 컬럼들은 모두 포함시켰고,

상관계수가 높은 컬럼들 우선적으로 진행했습니다.

저는 Atmosphere 컬럼을 맡았기 때문에 atmosphere 컬럼과 가장 상관관계가 높은 clean_store 컬럼을 제거했습니다.

 

# clean_store 컬럼 제거
df_ver_5_anal_ss_atmosphere_without_clean_store = df_ver_5_anal_ss.drop('clean_store', axis=1)

# K-Means
kmeans = KMeans(n_clusters=19, random_state=0).fit(df_ver_5_anal_ss_atmosphere_without_clean_store)
df_centroids = pd.DataFrame(kmeans.cluster_centers_, columns = df_ver_5_anal_ss_atmosphere_without_clean_store.columns)

# 군집별 사이즈 확인
label_counts = [c[x] for x in df_centroids.index]
df_centroids['size'] = label_counts
df_centroids

# Radar plot 그리기
import plotly.graph_objects as go

def plot_radar_from_centroid(df_centroids):
    df_centroids = df_centroids.drop('size', axis=1)
    fig = go.Figure()
    categories = df_centroids.columns
    for row in df_centroids.iterrows():
        fig.add_trace(go.Scatterpolar(
            r=row[1].tolist(),
            theta=categories,
            fill='toself',
            name='cluster {}'.format(row[0])
        ))

    fig.update_layout(
        autosize=False,
        width=1000,
        height=800,
    )
    fig.show()
    
    plot_radar_from_centroid(df_centroids)

이렇게 했을때 아래와 같이 Radar plot이 그려지는 것을 확인할 수 있는데요,

군집마다 특징이 있다는 것을 알 수 있습니다.

 

이제 각 군집에 속해있는 가게들이 실제 저 군집별 특성에 해당하는 가게인지를 확인해야했으므로

가게별, 군집별로 각각의 클러스터에 몇 번 속해있는지를 계산해서

가장 많이 속해있는 군집만 남기고 모두 지우는 작업을 진행했습니다.

new_df = df.pivot_table(index='Store', values = ['Review_score', 'Review_counts', 'bread', 'beverage', 'vegan', 'no_senior', 'no_kids',\
                                            'pet', 'whole_wheat', 'view', 'atmosphere', 'friendly', 'restroom_clean', 'parking', 'spacious_store', 'plenty_seats',\
                                                'comfortable_seats', 'special_menu', 'photo', 'good_music', 'reservation_possible', 'unique_concept', 'beautiful_interior',\
                                                    'price', 'wait', 'health', 'sentiment'],
                                        aggfunc='mean').reset_index()
new_df

# 'Store' 컬럼 추가
df_ver_5_anal_ss_atmosphere_without_clean_store['Store'] = df_ver_5_info['Store']

# 'Cluster' 컬럼 추가
df_ver_5_anal_ss_atmosphere_without_clean_store['cluster'] = kmeans.labels_

# groupby
df_grouped = df_ver_5_anal_ss_atmosphere_without_clean_store.groupby(['Store', 'cluster'])['Store'].count()
df_grouped = df_grouped.reset_index(name='Count')

# 최빈값만 남기기
df_grouped_sorted = df_grouped.sort_values(by=['Store', 'Count'], ascending=[True, False])
df_grouped_max_cluster = df_grouped_sorted.drop_duplicates(subset=['Store'])
df_grouped_max_cluster.drop('Count', axis=1, inplace=True)
df_grouped_max_cluster.sort_values('cluster', ascending=True, inplace=True)

df_grouped_max_cluster

이렇게 코드를 작성했을 때의 결과값은 아래와 같습니다.

 

cluster별로 store를 해당 가게의 이름을 네이버 지도에 검색한 후

해당 가게가 속한 군집의 특성에 적합한지를 판단했습니다.

일단 오늘은 한 개 컬럼만을 제거해서 군집 결과를 확인했는데요,

내일은 나머지 작업들을 진행해볼 예정입니다.