<jupyter_start><jupyter_text>[Tutorial] Opinion Review 문서 군집화 소개와 실습 ----<jupyter_code>import pandas as pd
import glob, os

path = r'/content/drive/My Drive/topics'
all_files = glob.glob(os.path.join(path, "*.data"))    
filename_list = []
opinion_text = []

# 개별 파일들의 파일명은 filename_list 리스트로 취합, 
# 개별 파일들의 파일내용은 DataFrame로딩 후 다시 string으로 변환하여 opinion_text 리스트로 취합 
for file_ in all_files:
    # 개별 파일을 읽어서 DataFrame으로 생성 
    df = pd.read_table(file_,index_col=None, header=0,encoding='latin1')
    
    # 절대경로로 주어진 file 명을 가공. 만일 Linux에서 수행시에는 아래 \\를 / 변경. 맨 마지막 .data 확장자도 제거
    filename_ = file_.split('/')[-1]
    filename = filename_.split('.')[0]

    #파일명 리스트와 파일내용 리스트에 파일명과 파일 내용을 추가. 
    filename_list.append(filename)
    opinion_text.append(df.to_string())

# 파일명 리스트와 파일내용 리스트를  DataFrame으로 생성
document_df = pd.DataFrame({'filename':filename_list, 'opinion_text':opinion_text})
document_df.head()
! pip install nltk
import nltk
import string
from nltk.stem import WordNetLemmatizer
nltk.download('wordnet')

remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
lemmar = WordNetLemmatizer()

def LemTokens(tokens):
    return [lemmar.lemmatize(token) for token in tokens]

def LemNormalize(text):
    return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))
# 각 파일 이름 자체만으로 의견의 텍스트가 어떤 제품/서비스에 대한 리뷰인 지 알 수 있음
# TF-IDF 형태로 피처 벡터화
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vect = TfidfVectorizer(tokenizer=LemNormalize,
                             stop_words='english',
                             ngram_range=(1,2),
                             min_df=0.05,
                             max_df=0.85)
# opinion_text 칼럼 값으로 피처 벡터화 수행
feature_vect = tfidf_vect.fit_transform(document_df['opinion_text'])<jupyter_output>/usr/local/lib/python3.6/dist-packages/sklearn/feature_extraction/text.py:385: UserWarning: Your stop_words may be inconsistent with your preprocessing. Tokenizing the stop words generated tokens ['ha', 'le', 'u', 'wa'] not in stop_words.
  'stop_words.' % sorted(inconsistent))<jupyter_text>* K-평균 군집화 기법을 적용합니다* 전자제품(네비게이션, 아이팟, 킨들, 랩탑 등), 자동차, 호텔으로 나뉨* 먼저 5개 중심 기반으로 어떻게 군집화되는 지 확인합니다<jupyter_code>from sklearn.cluster import KMeans

km_cluster = KMeans(n_clusters=5,
                    max_iter=10000,
                    random_state=0)
km_cluster.fit(feature_vect)
cluster_label = km_cluster.labels_
cluster_centers = km_cluster.cluster_centers_
# 각 데이터별로 할당된 군집의 레이블을 파일명과 파일 내용을 가지고 있는 df에 칼럼 추가해 저장함
document_df['cluster_label'] = cluster_label
document_df.head()
# 군집화 결과 확인하기
document_df[document_df['cluster_label']==0].sort_values(by='filename')<jupyter_output><empty_output><jupyter_text>* cluster 0 : 자동차<jupyter_code>document_df[document_df['cluster_label']==1].sort_values(by='filename')<jupyter_output><empty_output><jupyter_text>* cluster 1 : 호텔<jupyter_code>document_df[document_df['cluster_label']==2].sort_values(by='filename')<jupyter_output><empty_output><jupyter_text>* cluster 2 : 주로 네비게이션<jupyter_code>document_df[document_df['cluster_label']==3].sort_values(by='filename')<jupyter_output><empty_output><jupyter_text>* cluster 3 : 주로 소형 전자기기(아이팟, 노트북 등)<jupyter_code>document_df[document_df['cluster_label']==4].sort_values(by='filename')<jupyter_output><empty_output><jupyter_text>* cluster 4 : 자동차<jupyter_code># 중심 개수를 5개에서 3개로 낮추기
km_cluster = KMeans(n_clusters=3,
                    max_iter=10000,
                    random_state=0)
km_cluster.fit(feature_vect)
cluster_label = km_cluster.labels_
cluster_centers = km_cluster.cluster_centers_

# 소속 군집을 cluster_label로 할당, 해당 값으로 정렬
document_df['cluster_label'] = cluster_label
document_df.sort_values(by='cluster_label')<jupyter_output><empty_output><jupyter_text>* 자동차, 호텔, 전자기기 순으로 제대로 정렬되었습니다. --- 군집별 핵심 단어 추출하기<jupyter_code># 클러스터 속성값 확인
cluster_centers = km_cluster.cluster_centers_
print('cluster_centers shape: ', cluster_centers.shape)
print(cluster_centers)<jupyter_output>cluster_centers shape:  (3, 4611)
[[0.         0.00092551 0.         ... 0.         0.         0.        ]
 [0.         0.00099499 0.00174637 ... 0.         0.00183397 0.00144581]
 [0.01005322 0.         0.         ... 0.00706287 0.         0.        ]]<jupyter_text>* 3개의 군집에 word 피처가 4611개로 구성되어 있음* 각 행의 배열 값은 각 군집 내의 위치가 개별 중심과 얼마나 가까운가를 상대 값으로 나타낸 것* 군집별 핵심 단어를 찾아봅니다* ndarray의 argsort()[:, ::-1]를 이용하면 cluster_centers 배열 내 값이 큰 순으로 정렬된 위치 인덱스 값을 반환합니다.* 위치 인덱스 값이 필요한 이유는 핵심 단어 피처의 이름을 출력하기 위함입니다<jupyter_code># 군집별 top n 핵심 단어, 그 단어의 중심 위치 상댓값, 대상 파일명을 반환함
def get_cluster_details(cluster_model,
                        cluster_data,
                        feature_names,
                        clusters_num,
                        top_n_features=10):
  cluster_details = {}

  # cluster_centers array의 값이 큰 순으로 정렬된 인덱스 값을 반환
  # 군집 중심점별 할당된 word 피처들의 거리값이 큰 순으로 값을 구하기 위함
  centroid_feature_ordered_ind = cluster_model.cluster_centers_.argsort()[:,::-1]

  # 개별 군집 별로 반복하면서 핵심 단어, 그 단어의 중심 위치 상댓값, 대상 파일명 입력
  for cluster_num in range(clusters_num):
    # 개별 군집별 정보를 담을 데이터 초기화
    cluster_details[cluster_num] = {}
    cluster_details[cluster_num]['cluster'] = cluster_num

    # cluster_centers_.argsort()[:,::-1]로 구한 인덱스를 이용해 top n 피처 단어 구함
    top_feature_indexes = centroid_feature_ordered_ind[cluster_num, :top_n_features]
    top_features = [feature_names[ind] for ind in top_feature_indexes]

    # top_feature_indexes를 이용해 해당 피처 단어의 중심 위치 상댓값 구함
    top_feature_values = cluster_model.cluster_centers_[cluster_num,
                                                        top_feature_indexes].tolist()

    # cluster_details 딕셔너리 객체에 개별 군집별 핵심단어와 중심위치 상댓값, 해당 파일명 입력
    cluster_details[cluster_num]['top_features'] = top_features
    cluster_details[cluster_num]['top_features_value'] = top_feature_values
    filenames = cluster_data[cluster_data['cluster_label'] == cluster_num]['filename']
    filenames = filenames.values.tolist()

    cluster_details[cluster_num]['filenames'] = filenames

  return cluster_details                                   

# print 함수 만들기
def print_cluster_details(cluster_details):
  for cluster_num, cluster_detail in cluster_details.items():
    print('##### Cluster {0}'.format(cluster_num))
    print('Top Features: ', cluster_detail['top_features'])
    print('Reviews 파일명: ', cluster_detail['filenames'][:7])
    print('==============================================')
feature_names = tfidf_vect.get_feature_names()

cluster_details = get_cluster_details(cluster_model = km_cluster,
                                      cluster_data = document_df,
                                      feature_names = feature_names,
                                      clusters_num = 3,
                                      top_n_features = 10)

print_cluster_details(cluster_details)<jupyter_output>##### Cluster 0
Top Features:  ['interior', 'seat', 'mileage', 'comfortable', 'gas', 'gas mileage', 'transmission', 'car', 'performance', 'quality']
Reviews 파일명:  ['gas_mileage_toyota_camry_2007', 'performance_honda_accord_2008', 'quality_toyota_camry_2007', 'comfort_toyota_camry_2007', 'interior_honda_accord_2008', 'mileage_honda_accord_2008', 'seats_honda_accord_2008']
==============================================
##### Cluster 1
Top Features:  ['room', 'hotel', 'service', 'staff', 'food', 'location', 'bathroom', 'clean', 'price', 'parking']
Reviews 파일명:  ['staff_bestwestern_hotel_sfo', 'location_holiday_inn_london', 'room_holiday_inn_london', 'parking_bestwestern_hotel_sfo', 'free_bestwestern_hotel_sfo', 'rooms_bestwestern_hotel_sfo', 'location_bestwestern_hotel_sfo']
==============================================
##### Cluster 2
Top Features:  ['screen', 'battery', 'keyboard', 'battery life', 'life', 'kindle', 'direction', 'video', 'size', 'voice']
Reviews 파일명:  ['screen_garmin_nu[...]
{'fraction_non_alphanumeric': 0.12699629487670883, 'fraction_numerical': 0.021591925386482688, 'mean_word_length': 3.8986232790988735, 'pattern_counts': {'":': 0, '<': 34, '<?xml version=': 0, '>': 34, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '499084', 'license': 'MIT', 'n_tokens_mistral': 3472, 'n_tokens_neox': 3847, 'n_words': 597, 'path': 'python_ml_08_opinion_review.ipynb', 'repo_name': 'ameliachoi/tutorial-python-machine-learning', 'size': '7827'}