일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 자연어분석
- 챗gpt
- 사이킷런
- HTML
- 판다스
- deeplearning
- chatGPT
- NLP
- langchain
- 판다스 데이터정렬
- pandas
- 딥러닝
- fastapi #python웹개발
- pytorch
- programmablesearchengine
- python 정렬
- 파이썬
- fastapi #파이썬웹개발
- sklearn
- MachineLearning
- 파이토치
- fastapi
- 랭체인
- OpenAIAPI
- 파이썬웹개발
- 머신러닝
- Python
- 비지도학습
- 파이토치기본
- konlpy
- Today
- Total
Data Navigator
[NLP, sklearn] 호텔스 닷컴 사용자 후기 및 평점 분석, 댓글 감성 분석 예측 모델 - Naive Bayes Classification 본문
[NLP, sklearn] 호텔스 닷컴 사용자 후기 및 평점 분석, 댓글 감성 분석 예측 모델 - Naive Bayes Classification
코딩하고분석하는돌스 2021. 2. 19. 01:16
NLP 호텔스닷컴 사용자 후기와 평점 분석 sklearn의 Naive Bayes Classification을 이용
1. 분석 목적
호텔스 닷컴에서 수집한 호텔에 대한 평점과 사용자 리뷰 간의 관계를 파악하고 리뷰 글을 분석해서 평점이 긍정적으로 나올지, 아닐지를 판별하는 예측 모델을 만든다.
2. 분석 대상 데이터
호텔스 닷컴에서 수집한 전국 8884개 호텔, 사용자 리뷰 89,942개
3. 분석 결과
1) 사용자 리뷰 키워드 분석 내용
전체적으로 객실, 친절, 조식, 직원 등의 빈도가 높았고 객실 상태, 직원의 친절한 응대, 조식 음식의 맛과 질이
호텔 사용자 만족도에 크게 영향을 끼침을 알 수 있었음.
A. '만족'한 그룹의 사용자 리뷰에서 가장 높은 빈도수를 나타낸 단어는 [친절], [편안], [위치], [조식] 으로 나타남
친절함과 편안함 위치 조식 등에 대한 언급이 많은 것으로 보아 호텔 직원들의 응대가 사용자 만족도에 매우
중요함을 알 수 있고, 편안한 침대, 호텔 위치 그리고 음식의 질도 만족도에 중요함을 알 수 있다.
B. '불만족'한 그룹에서는 [가격, 대비], [모텔], [방], [조식], [사진], [방음] 에 대한 언급이 많았음.
불만족한 그룹의 주요 불만 사항은 지불한 금액에 비해서 호텔의 시설이 열악한 것에 대한 내용이 많음을 알 수
있었다. 가장 많이 언급된 단어가 [가격대비], [모텔] 이며 추가로 [사진] 이 같이 나오는 것으로 보아 사진에는
시설이 좋게 나와있지만 실제로는 열악했고 심지어 가격은 비싼데 모델 보다 시설이 좋지 않거나 한 상황에
대한 불만이 크다고 볼 수 있다.
2)키워드 분석을 통한 만족도 향상에 영향을 주는 요소
키워드 분석을 종합해 보면 호텔 이용시 가장 기본이 되는 부분은 가격 대비 시설의 상태임을 알 수 있다. '만족'
그룹에서 [친절], [편안], [위치], [조식] 등이 많이 언급되지만, 그것은 '불만족' 그룹에서 가장 많이 언급된
[가격대비], [모텔], [사진] 의 단어처럼 호텔의 격과 가격에 걸맞는 시설을 갖추고 있어야 하는 것이 전제 되기
때문이다. 특히 [사진]이라는 단어가 언급된 것으로 보아, 예약시 사진으로 본 객실과의 상태 차이가 클 경우
만족도에 큰 영향을 미칠 것으로 예측된다.
3) 사용자 리뷰를 통해서 분석한 내용을 토대로 볼 때 호텔이 고객의 만족도를 높이기 위해서 필요한 것
A. 호텔 시설 상태를 왜곡한 과장된 사진을 쓰지 않는 것이 좋다.
a. 호텔 시설의 상태는 소비자가 지불할 적정한 가격을 산정하는데 중요한 고려 요소가 되므로
시설이 좋지 않더라도 가격이 적당하다면 그 부분은 감수하고 이용할 수도 있다.
b. 손님을 끌어들이기 위해서 과장된 사진을 사용하면 최초 고객 유입에는 성공할 수 있을 것이다.
그러나 이용시 만족도를 떨어뜨리고, 나쁜 평가를 받게 되며, 재이용률을 떨어뜨린다.
B. 청소 상태도 중요하다.
'불만족' 그룹 키워드 중 상위는 아니었지만 [담배냄새], [청소상태]에 대한 키워드도 많았던 것으로 보아
호텔 시설은 청소상태도 포함됨을 유추할 수 있다.
C. 가격에 맞는 시설과 깔끔한 청소 상태가 갖추어져 있다면 [친절함]과 [조식]에 신경을 쓰자.
'만족' 그룹에서 가장 많이 언급된 단어는 [친절]이다. 기본이 갖추어진 호텔에서 고객의 만족도를 높이는
가장 중요한 요소는 직원의 빠르고 친절한 응대 이다. 그리고 [조식]에 대한 만족도도 상당히 높은 비중을
차지하고 있으므로 직원의 친절함과 맛있는 조식에 신경을 쓴다면 사용자의 만족도는 더 높아 질 것이다.
4. 나이브 베이즈 알고리즘을 이용한 예측 모델 분석 결과
1) accuracy_score: 0.8631941742175774 (86% 의 예측률)
2) 컨퓨전 메트릭스 결과
confusion_matrix: array([[ 2368, 1628],
[833, 13160]])
컨퓨전 매트릭스의 결과를 볼 때 '만족'으로 예측했으나 '불만족'이었던 FP는 1,628 (제1오류),
'불만족'으로 예측했으나 '만족'이었던 FN은 833 (제2오류) 으로 제 1 오류에 대한 정확도가 떨어졌다.
3) classification_report 으로 precision, recall, f1-schore를 체크
아래의 결과에서 볼 수 있듯이 역시 제1오류에 대한 recall값과 f1-score가 낮게 나온 것을 볼 수 있다.
호텔의 입장에서는 '만족'으로 예측했으나 '불만족'이었던 경우(제1오류)가 '불만족'으로 예측했으나 '만족'이었던 (제2오류)보다
더 중요하므로 현재 분석 모델은 보완이 필요하다.
precision recall f1-score support
0 0.74 0.59 0.66 3996
1 0.89 0.94 0.91 13993
accuracy 0.86 17989
macro avg 0.81 0.77 0.79 17989
weighted avg 0.86 0.86 0.86 17989
5. 상세 분석 내용
1) 전체 사용자 리뷰를 분석한 결과 키워드
빈도수는 '방': 16973, '친절': 16504, '만족': 15373, '시설': 13939, '조식': 13008, '직원': 12765,
'이용': 12177, '위치': 10663, '객실': 10149, '주변': 9433 의 순으로 나타남
2) 평점을 기준으로 '만족', '불만족'으로 분리
사용자 평점 분포의 25%~75% 가 8 이상이므로 8이상의 점수를 준 고객을 '만족', 7이하를 불만족으로 정의.
두 그룹으로 나누어 다시 키워드를 분석함.
3) 사용자 평점 8 이상의 '만족' 고객의 리뷰에서 나온 키워드 순위
[만족] 821, [친절] 259, [편안] 225, [위치] 145, [직원, 친절] 109, [조식] 108, [가격, 대비] 106, [전반] 100,
[전반, 만족] 93, [시설] 81, [가성, 비] 80, [호텔] 79, [뷰] 79, [숙소] 74, [방] 69, [청결] 68, [가격, 대비, 만족] 62
4) 사용자 평점 7 이하의 '불만족' 고객의 리뷰에서 나온 키워드 순위
[가격, 대비] 23, [모텔] 23, [보통] 20, [방] 19, [가격] 17, [만족] 17, [조식] 16, [시설] 16, [사진] 14, [방음] 14,
[모텔, 수준] 14, [호텔, 모텔] 13, [호텔] 13, [가성, 비] 12, [침대, 불편] 11, [최악] 10, [불친절] 10, [숙소] 10,
[위치] 10, [전반] 9, [담배, 냄새] 8, [생각] 8, [청소, 상태] 8, [객실] 8, [청결] 7, [침대] 7, [화장실] 6, [불편] 6
6. 분석방법
1) 사용자 리뷰 키워드 분석 및 워드 클라우드 시각화 (핵심 단어를 한 눈에 살펴 봄으로써 인사이트 도출)
A. Python의 pandas로 데이터를 정제한 후 KoNLPy의 Mecob 토크나이저를 사용하여 명사만 추출
B. NLTK의 FreqDist로 단어 빈도 추출
C. wordcloud를 사용하여 주요 키워드 시각화
2) Naive Bayes 알고리즘을 사용하여 사용자 리뷰의 단어를 통해 만족도를 예측하는 감성 예측 모델 생성
A. sklearn의 CountVectorizer로 사용자 리뷰를 벡터화
B. sklearn의 Train_test_split으로 8:2의 비율로 학습 데이터와 테스트 데이터로 분리
C. sklearn의 MultinomialNB 알고리즘으로 감성 예측 모델 생성
3) 모델 검증
A. accuracy_score, confusion_matrix, classification_report 를 사용하여 분석 결과 검증
7. 분석환경
1) 자료수집: Windows 10, python, pandas
2) 자료 전처리 및 분석: Linux Ubuntu 20.02, Python, Pandas, KoNLPy, Mecab, wordcloud, sklearn
8. 데이터 전처리 및 분석 과정 전체 코드
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
data = pd.read_csv('./01_Real_Real_Final_Hotel_KoreanReviewTexts_89942.csv', encoding='utf-8')
2. EDA¶
data.head(5)
Unnamed: 0 | hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | isKorNot | |
---|---|---|---|---|---|---|---|---|---|
0 | 356 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | 직원들 서비스나 마인드 완벽합니다 다만 노후된 시설과 좁은 라운지가 호텔 이름에 비... | True |
1 | 357 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | 웨스틴조선은 모든면에서 최고의 숙소라고 생각합니다 분위기 편의성 직원친절도까지 모두... | True |
2 | 358 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | other | 2박 여행 | 2019년 2월 2일 | 10.0 | 서울시내에서 호캉스를 누릴 수 있는 곳아침 뷔페가 맛있는 곳룸서비스가 좋았음요구사항... | True |
3 | 359 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | romance | 1박 로맨틱 여행 | 2019년 1월 26일 | 10.0 | 가격에 맞게 만족했습니다 | True |
4 | 360 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | with-friends | 친구와 1박 여행 | 2019년 1월 25일 | 10.0 | 처음 세명으로 셋팅 되어있지 않은점 빼고는 직원들의 친절도 및 서비스는 모두 맘에 ... | True |
불필요한 컬럼 삭제¶
data.drop(['Unnamed: 0', 'isKorNot'], axis=1, inplace=True)
data.head(3)
hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | |
---|---|---|---|---|---|---|---|
0 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | 직원들 서비스나 마인드 완벽합니다 다만 노후된 시설과 좁은 라운지가 호텔 이름에 비... |
1 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | 웨스틴조선은 모든면에서 최고의 숙소라고 생각합니다 분위기 편의성 직원친절도까지 모두... |
2 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | other | 2박 여행 | 2019년 2월 2일 | 10.0 | 서울시내에서 호캉스를 누릴 수 있는 곳아침 뷔페가 맛있는 곳룸서비스가 좋았음요구사항... |
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 89941 entries, 0 to 89940
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 hotelId 89941 non-null int64
1 hotelName 89941 non-null object
2 tripType 89941 non-null object
3 tripTypeText 89941 non-null object
4 reviewDate 89941 non-null object
5 rating 89941 non-null float64
6 description 89941 non-null object
dtypes: float64(1), int64(1), object(5)
memory usage: 4.8+ MB
사용자 리뷰 컬럼의 글자수 길이를 체크해서 새로운 컬럼으로 추가¶
data['text_length'] = data['description'].apply(len)
data.head(5)
hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | text_length | |
---|---|---|---|---|---|---|---|---|
0 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | 직원들 서비스나 마인드 완벽합니다 다만 노후된 시설과 좁은 라운지가 호텔 이름에 비... | 56 |
1 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | 웨스틴조선은 모든면에서 최고의 숙소라고 생각합니다 분위기 편의성 직원친절도까지 모두... | 55 |
2 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | other | 2박 여행 | 2019년 2월 2일 | 10.0 | 서울시내에서 호캉스를 누릴 수 있는 곳아침 뷔페가 맛있는 곳룸서비스가 좋았음요구사항... | 91 |
3 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | romance | 1박 로맨틱 여행 | 2019년 1월 26일 | 10.0 | 가격에 맞게 만족했습니다 | 13 |
4 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | with-friends | 친구와 1박 여행 | 2019년 1월 25일 | 10.0 | 처음 세명으로 셋팅 되어있지 않은점 빼고는 직원들의 친절도 및 서비스는 모두 맘에 ... | 112 |
data.describe()
hotelId | rating | text_length | |
---|---|---|---|
count | 8.994100e+04 | 89941.000000 | 89941.000000 |
mean | 3.350734e+08 | 8.187034 | 84.905872 |
std | 4.732087e+08 | 2.197401 | 95.335087 |
min | 1.053430e+05 | 2.000000 | 1.000000 |
25% | 4.661100e+05 | 8.000000 | 30.000000 |
50% | 6.001780e+05 | 8.000000 | 59.000000 |
75% | 6.875979e+08 | 10.000000 | 104.000000 |
max | 1.966253e+09 | 10.000000 | 1472.000000 |
rating(평점)의 분포를 그래프로 확인¶
plt.figure(figsize=(5,5))
sns.distplot(data['rating'])
/home/haram4th/anaconda3/envs/mdai/lib/python3.8/site-packages/seaborn/distributions.py:2557: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms).
warnings.warn(msg, FutureWarning)
<AxesSubplot:xlabel='rating', ylabel='Density'>
plt.figure(figsize=(5,5))
sns.boxplot(data['rating'])
/home/haram4th/anaconda3/envs/mdai/lib/python3.8/site-packages/seaborn/_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation.
warnings.warn(
<AxesSubplot:xlabel='rating'>
평점의 대부분이 8~10 사이, 8미만은 많지 않음¶
사용자 리뷰 글자수¶
plt.figure(figsize=(5,5))
sns.distplot(data['text_length'])
/home/haram4th/anaconda3/envs/mdai/lib/python3.8/site-packages/seaborn/distributions.py:2557: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms).
warnings.warn(msg, FutureWarning)
<AxesSubplot:xlabel='text_length', ylabel='Density'>
data['text_length'].describe()
count 89941.000000
mean 84.905872
std 95.335087
min 1.000000
25% 30.000000
50% 59.000000
75% 104.000000
max 1472.000000
Name: text_length, dtype: float64
평균 84자, 최소 1자, 최대 1472¶
data.corr()
hotelId | rating | text_length | |
---|---|---|---|
hotelId | 1.000000 | 0.044247 | 0.018079 |
rating | 0.044247 | 1.000000 | -0.190432 |
text_length | 0.018079 | -0.190432 | 1.000000 |
3. 사용자 리뷰 컬럼 데이터 전처리¶
String 모듈의 string.punctuation 로 특수문자 제거¶
import string
string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
data['description'] = data['description'].apply(lambda x : ''.join([i for i in x if i not in string.punctuation]))
data2 = data.copy()
data.head()
hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | text_length | |
---|---|---|---|---|---|---|---|---|
0 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | 직원들 서비스나 마인드 완벽합니다 다만 노후된 시설과 좁은 라운지가 호텔 이름에 비... | 56 |
1 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | 웨스틴조선은 모든면에서 최고의 숙소라고 생각합니다 분위기 편의성 직원친절도까지 모두... | 55 |
2 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | other | 2박 여행 | 2019년 2월 2일 | 10.0 | 서울시내에서 호캉스를 누릴 수 있는 곳아침 뷔페가 맛있는 곳룸서비스가 좋았음요구사항... | 91 |
3 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | romance | 1박 로맨틱 여행 | 2019년 1월 26일 | 10.0 | 가격에 맞게 만족했습니다 | 13 |
4 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | with-friends | 친구와 1박 여행 | 2019년 1월 25일 | 10.0 | 처음 세명으로 셋팅 되어있지 않은점 빼고는 직원들의 친절도 및 서비스는 모두 맘에 ... | 112 |
KoNLPy로 토큰화¶
from konlpy.tag import Mecab
mecab = Mecab()
print(mecab.nouns(data['description'][8]))
['위치', '룸', '컨디션', '조', '식부', '폐', '음식', '퀄리티']
data['description'] = data['description'].apply(lambda x: mecab.nouns(x))
data['description'].head()
0 [직원, 서비스, 마인드, 완벽, 노후, 시설, 라운지, 호텔, 이름, 부족]
1 [웨스틴, 조선, 면, 최고, 숙소, 생각, 분위기, 편의, 직원, 친절, 인상]
2 [서울, 시내, 호, 수, 곳, 아침, 뷔페, 곳, 룸서비스, 요구, 사항, 응대,...
3 [가격, 만족]
4 [처음, 명, 셋, 점, 직원, 친절, 서비스, 맘, 시설, 불편, 추가, 커피, ...
Name: description, dtype: object
data.iloc[0]['description']
['직원', '서비스', '마인드', '완벽', '노후', '시설', '라운지', '호텔', '이름', '부족']
불용어 처리 후 전체 단어 등장 빈도 확인¶
stopwords = pd.read_csv('한국어불용어100.txt', encoding='utf-8', sep='\t')
stopwords.head()
이 | VCP | 0.018279601 | |
---|---|---|---|
0 | 있 | VA | 0.011699 |
1 | 하 | VV | 0.009774 |
2 | 것 | NNB | 0.009733 |
3 | 들 | XSN | 0.006898 |
4 | 그 | MM | 0.005327 |
stopwords.columns = ['words','pos','ratio']
stopword = list(stopwords['words'])
호텔이 너무 많이 나와 불용어에 추가¶
stopword.append('호텔')
#stopword
all_word = []
for i in data['description']:
for j in i:
if j not in stopword:
all_word.append(j)
else:
continue
len(all_word)
1038772
from nltk.probability import FreqDist
FreqDist(all_word)
FreqDist({'방': 16973, '친절': 16504, '만족': 15373, '시설': 13939, '조식': 13008, '직원': 12765, '이용': 12177, '위치': 10663, '객실': 10149, '주변': 9433, ...})
4. Word Cloud 로 시각화 및 키워드 분석¶
from wordcloud import WordCloud
전체 키워드에 대한 워드 클라우드¶
fontpath = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
wc = WordCloud(
width = 600,
height = 600,
max_words=20000,
font_path = fontpath,
background_color='white'
).generate(str(all_word))
plt.figure(figsize=(20, 10))
plt.imshow(wc)
plt.axis('off')
(-0.5, 599.5, 599.5, -0.5)
good = data[data['rating'] >= 8]['description']
good
0 [직원, 서비스, 마인드, 완벽, 노후, 시설, 라운지, 호텔, 이름, 부족]
1 [웨스틴, 조선, 면, 최고, 숙소, 생각, 분위기, 편의, 직원, 친절, 인상]
2 [서울, 시내, 호, 수, 곳, 아침, 뷔페, 곳, 룸서비스, 요구, 사항, 응대,...
3 [가격, 만족]
4 [처음, 명, 셋, 점, 직원, 친절, 서비스, 맘, 시설, 불편, 추가, 커피, ...
...
89935 [주차료, 하루, 만, 원, 칫솔, 치약, 구비, 주말, 경우, 직원, 명, 체크인...
89936 [매니저, 친절, 상권, 근처, 편리]
89938 [목적지, 인근, 숙소, 것, 예약, 저녁, 시, 도착, 주변, 당황, 때, 것, ...
89939 [호텔, 편안, 오픈, 얼마, 관리, 침구, 편안, 조식, 뷔페, 기대, 샐러드, ...
89940 [소음]
Name: description, Length: 70258, dtype: object
good.value_counts().head(30)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.map_locations()
TypeError: unhashable type: 'list'
Exception ignored in: 'pandas._libs.index.IndexEngine._call_map_locations'
Traceback (most recent call last):
File "pandas/_libs/hashtable_class_helper.pxi", line 4588, in pandas._libs.hashtable.PyObjectHashTable.map_locations
TypeError: unhashable type: 'list'
[] 2468
[만족] 821
[친절] 259
[편안] 225
[위치] 145
[직원, 친절] 109
[조식] 108
[가격, 대비] 106
[전반] 100
[전반, 만족] 93
[시설] 81
[가성, 비] 80
[호텔] 79
[뷰] 79
[숙소] 74
[방] 69
[청결] 68
[가격, 대비, 만족] 62
[굿] 55
[곳] 47
[전체, 만족] 44
[최고] 43
[서비스] 43
[전체] 42
[가족, 여행] 40
[사장, 친절] 39
[가격] 38
[생각] 34
[안락] 32
[전망] 31
Name: description, dtype: int64
bad = data[data['rating'] <= 7]['description']
bad
15 [가성, 별로, 기대, 실망, 예약, 사전, 메일, 직원, 통화, 객실, 요청, 고...
30 [번, 호텔, 방문, 것, 예전, 호텔, 이번, 실망, 주변, 호텔, 앞, 시위, ...
35 [서비스, 절도, 프로, 느낌]
41 [뷰, 처음, 국내외, 특급, 호텔, 곳, 중, 최악, 당일, 예약, 데이터, 핑계...
43 [고객, 개, 법, 호텔, 역사, 명불허전, 종업원, 모두, 최상, 서비스, 무엇,...
...
89924 [엘레, 베, 이터, 불편]
89928 [객실, 내부, 욕실, 내부, 호텔]
89929 [대체, 만족, 점수, 건, 제, 다음, 출장, 이용, 겁니다]
89932 [샤워기, 수압]
89937 [주차, 유로, 출차, 시간, 불편]
Name: description, Length: 19683, dtype: object
bad.value_counts().head(30)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.map_locations()
TypeError: unhashable type: 'list'
Exception ignored in: 'pandas._libs.index.IndexEngine._call_map_locations'
Traceback (most recent call last):
File "pandas/_libs/hashtable_class_helper.pxi", line 4588, in pandas._libs.hashtable.PyObjectHashTable.map_locations
TypeError: unhashable type: 'list'
[] 228
[가격, 대비] 23
[모텔] 23
[보통] 20
[방] 19
[가격] 17
[만족] 17
[조식] 16
[시설] 16
[사진] 14
[방음] 14
[모텔, 수준] 14
[호텔, 모텔] 13
[호텔] 13
[가성, 비] 12
[침대, 불편] 11
[최악] 10
[불친절] 10
[숙소] 10
[위치] 10
[전반] 9
[담배, 냄새] 8
[생각] 8
[청소, 상태] 8
[객실] 8
[청결] 7
[침대] 7
[가격, 대비, 만족] 7
[화장실] 6
[불편] 6
Name: description, dtype: int64
rating이 8이상인 사용자 리뷰의 워드 클라우드¶
wc = WordCloud(
width = 600,
height = 600,
max_words=20000,
font_path = fontpath,
background_color='white',
stopwords=['호텔']
).generate(str(good))
plt.figure(figsize=(20, 10))
plt.imshow(wc)
plt.axis('off')
(-0.5, 599.5, 599.5, -0.5)
rating이 7이하인 사용자 리뷰의 워드 클라우드¶
wc = WordCloud(
width = 600,
height = 600,
max_words=20000,
font_path = fontpath,
background_color='white',
stopwords=['호텔']
).generate(str(bad))
plt.figure(figsize=(20, 10))
plt.imshow(wc)
plt.axis('off')
(-0.5, 599.5, 599.5, -0.5)
data.head(2)
hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | text_length | |
---|---|---|---|---|---|---|---|---|
0 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | [직원, 서비스, 마인드, 완벽, 노후, 시설, 라운지, 호텔, 이름, 부족] | 56 |
1 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | [웨스틴, 조선, 면, 최고, 숙소, 생각, 분위기, 편의, 직원, 친절, 인상] | 55 |
5. Naive Bayes 알고리즘으로 모델링 및 예측 모델 생성¶
data2.head(2)
hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | text_length | |
---|---|---|---|---|---|---|---|---|
0 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | 직원들 서비스나 마인드 완벽합니다 다만 노후된 시설과 좁은 라운지가 호텔 이름에 비... | 56 |
1 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | 웨스틴조선은 모든면에서 최고의 숙소라고 생각합니다 분위기 편의성 직원친절도까지 모두... | 55 |
rating에 있는 사용자 리뷰중 8 이상은 1(good), 7이하는 0(bad)로 변환해서 goodorbad 컬럼 추가
def goodorbad(x):
if x >= 8:
return 1
else:
return 0
data2['goodorbad'] = data2['rating'].apply(lambda x: goodorbad(x))
data2.head(2)
hotelId | hotelName | tripType | tripTypeText | reviewDate | rating | description | text_length | goodorbad | |
---|---|---|---|---|---|---|---|---|---|
0 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 1월 26일 | 10.0 | 직원들 서비스나 마인드 완벽합니다 다만 노후된 시설과 좁은 라운지가 호텔 이름에 비... | 56 | 1 |
1 | 105343 | 서울 웨스틴조선호텔 (The Westin Chosun Seoul) | family | 1박 가족 여행 | 2019년 2월 4일 | 10.0 | 웨스틴조선은 모든면에서 최고의 숙소라고 생각합니다 분위기 편의성 직원친절도까지 모두... | 55 | 1 |
X = data2['description']
y = data2['goodorbad']
Count Vectorizer로 description에 있는 단어를 벡터화¶
type(X)
pandas.core.series.Series
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(lowercase=False)
cv.fit(X)
CountVectorizer(lowercase=False)
X = cv.transform(X)
Train data와 test data로 나누기¶
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.2, random_state=100)
y_test
17971 0
10704 0
66443 1
60061 1
13005 0
..
80102 0
69208 1
1686 1
19211 1
56643 1
Name: goodorbad, Length: 17989, dtype: int64
Naive Bayes 알고리즘으로 모델링¶
from sklearn.naive_bayes import MultinomialNB
model = MultinomialNB()
model.fit(X_train, y_train)
MultinomialNB()
pred = model.predict(X_test)
예측 결과 평가¶
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
accuracy_score(y_test, pred)
0.8631941742175774
confusion_matrix(y_test, pred)
array([[ 2368, 1628],
[ 833, 13160]])
print(classification_report(y_test, pred))
precision recall f1-score support
0 0.74 0.59 0.66 3996
1 0.89 0.94 0.91 13993
accuracy 0.86 17989
macro avg 0.81 0.77 0.79 17989
weighted avg 0.86 0.86 0.86 17989