728x90
반응형

review_data.csv
0.01MB

import pandas as pd
df = pd.read_csv('review_data.csv')
df

	score	review	y
0	1	예약할 때는 룸을 주기로 하고 홀을 주고, 덥고, 직원들이 정신이 없어 그 가격에 ...	0
1	5	점심식사 잘했던곳.후식커피한잔 하기도 좋고 주차가능합니다. 음식 맛있고 직원분 친절...	1
2	5	新鮮でおいしいです。	1
3	4	녹는다 녹아	1
4	4	NaN	1
...	...	...	...
75	2	이렇게 대기가 긴 맛집인줄 모르고 갔다가 엄청 기다림 예써라는 어플로 대기 하던데 ...	0
76	1	단짠의 정석. 진짜 정석으로 달고 짬. 질리는 맛. 사장님이랑 와이프로 추정되는 ...	0
77	4	만족스러움! 맛있어용	1
78	1	곱창은 없고 대창만 들어있어서 느끼한데 양념은 너무 매워서 위에 탈이나 고생했습니다ㅠㅠ	0
79	5	대창덮밥도 맛있고 곱도리탕도 맛나요 완전 소주각입니다. 자리가 쫍아서 테이블마다 ...	1
80 rows × 3 columns

 

import re
def text_cleaning(text) :
    hangul = re.compile('[^ ㄱ-ㅣ가-힣]+')
    result = hangul.sub('', text)
    return result
text_cleaning("abc가나다123 라마사아 123")

'가나다 라마사아 '

 

df['ko_text'] = df['review'].apply(lambda x : text_cleaning(str(x))) # null 값
df['ko_text']

0     예약할 때는 룸을 주기로 하고 홀을 주고 덥고 직원들이 정신이 없어 그 가격에 내가...
1     점심식사 잘했던곳후식커피한잔 하기도 좋고 주차가능합니다 음식 맛있고 직원분 친절하여...
2                                                      
3                                                녹는다 녹아
4                                                      
                            ...                        
75    이렇게 대기가 긴 맛집인줄 모르고 갔다가 엄청 기다림 예써라는 어플로 대기 하던데 ...
76    단짠의 정석 진짜 정석으로 달고 짬 질리는 맛  사장님이랑 와이프로 추정되는 서빙해...
77                                           만족스러움 맛있어용
78    곱창은 없고 대창만 들어있어서 느끼한데 양념은 너무 매워서 위에 탈이나 고생했습니다ㅠㅠ 
79    대창덮밥도 맛있고 곱도리탕도 맛나요 완전 소주각입니다  자리가 쫍아서 테이블마다 가...
Name: ko_text, Length: 80, dtype: object

 

df['review'].head()

0    예약할 때는 룸을 주기로 하고 홀을 주고, 덥고, 직원들이 정신이 없어 그 가격에 ...
1    점심식사 잘했던곳.후식커피한잔 하기도 좋고 주차가능합니다. 음식 맛있고 직원분 친절...
2                                           新鮮でおいしいです。
3                                               녹는다 녹아
4                                                  NaN
Name: review, dtype: object

 

df1 = df.loc[df['ko_text'].apply(lambda x : len(x)) > 0]
df1.isnull().value_counts()

score  review  y      ko_text
False  False   False  False      65
dtype: int64

 

del df['review']
df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 80 entries, 0 to 79
Data columns (total 3 columns):
 #   Column   Non-Null Count  Dtype 
---  ------   --------------  ----- 
 0   score    80 non-null     int64 
 1   y        80 non-null     int64 
 2   ko_text  80 non-null     object
dtypes: int64(2), object(1)
memory usage: 2.0+ KB

 

from konlpy.tag import Okt

# 텍스트 데이터 형태소 추출
def get_pos(x) :
    tagger = Okt()
    pos = tagger.pos(x)
    # word : konlpy 모듈 형태소 분석단어
    # tag : 형태소 분석된 품사
    pos = ['{0}/{1}'.format(word, tag) for word, tag in pos]
    return pos

result = get_pos(df['ko_text'].values[0])
print(result)

['예약/Noun', '할/Verb', '때/Noun', '는/Josa', '룸/Noun', '을/Josa', '주기/Noun', '로/Josa', '하고/Verb', '홀/Noun', '을/Josa', '주고/Verb', '덥고/Adjective', '직원/Noun', '들/Suffix', '이/Josa', '정신/Noun', '이/Josa', '없어/Adjective', '그/Noun', '가격/Noun', '에/Josa', '내/Noun', '가/Josa', '직접/Noun', '구워/Verb', '먹고/Verb', '갈비살/Noun', '등심/Noun', '은/Josa', '질/Noun', '기고/Noun', '냉면/Noun', '은/Josa', '맛/Noun', '이/Josa', '없고/Adjective', '장어/Noun', '양념/Noun', '들/Suffix', '도/Josa', '제/Noun', '때/Noun', '안/Noun', '가져다/Verb', '주고/Verb', '회식/Noun', '으로/Josa', '한/Determiner', '시간/Noun', '만에/Josa', '만원/Noun', '을/Josa', '썼는데/Verb', '이런/Adjective', '경험/Noun', '처음/Noun', '입니다/Adjective']

 

from sklearn.feature_extraction.text import CountVectorizer
                     #글뭉치(corpus) 인덱스로 생성
index_vectorizer = CountVectorizer(tokenizer = lambda x : get_pos(x))
# 
 # 형태소분석하고 단어품사 분리
x = index_vectorizer.fit_transform(df['ko_text'].tolist())
x.shape

# (80, 779)

 

from sklearn.feature_extraction.text import TfidfTransformer
# 글뭉치, 형태소분석의 단어
tfidf_vectorizer =  TfidfTransformer()
x = tfidf_vectorizer.fit_transform(x)
print(x.shape)

# (80, 779)

 

# 긍부정 리뷰분류
# 데이터셋 분리
from sklearn.model_selection import train_test_split
y = df['y']
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.30)

 

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(random_state = 0)
lr.fit(x_train, y_train)
y_pred = lr.predict(x_test)

 

x_train.shape
# (56, 779)

len(lr.coef_[0])
# 779

 

import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [10, 8]
plt.bar(range(len(lr.coef_[0])), lr.coef_[0])

 

# lr.coef_[0] 내림차순
# 상위 양수 : 긍정적인 단어, 긍정가중치
sorted(((value, index) for index, value in enumerate(lr.coef_[0])), reverse=True)[:5]

[(0.31921037916122147, 269),
 (0.31181674718077157, 266),
 (0.31181674718077157, 2),
 (0.22722099938708767, 778),
 (0.22499528665817484, 719)]

 

# 하위 음수 : 부정적인 단어, 부정 가중치값 5개
sorted(((value, index) for index, value in enumerate(lr.coef_[0])), reverse=True)[-5:]

[(-0.3303223649310512, 736),
 (-0.35074686047120107, 374),
 (-0.35074686047120107, 80),
 (-0.3756982096297823, 538),
 (-0.3907079128326151, 147)]

 

coef_pos_index = sorted(((value, index) for index, value in enumerate(lr.coef_[0])), reverse=True)
invert_index_vectorizer = { v : k for k, v in index_vectorizer.vocabulary_.items()}
cnt = 0
for k, v in index_vectorizer.vocabulary_.items() :
    print(k, v)
    cnt += 1
    if cnt >= 10 :
        break
# index_vectorizer 글뭉치 데이터
# invert_index_vectorizer 피처 인덱스 : 단어 / 품사

예약/Noun 504
할/Verb 743
때/Noun 224
는/Josa 162
룸/Noun 236
을/Josa 538
주기/Noun 631
로/Josa 235
하고/Verb 721
홀/Noun 769

 

# 회귀모델의 계수를 index_vectorizer에 맵핑하여, 어떤 형태소인지 출력
for coef in coef_pos_index[:20] :
    print(invert_index_vectorizer[coef[1]], coef[0]) # 단어 품사
#      피처 인덱스값 가져와서, ceof 가중치 가져오기

맛있어요/Adjective 0.31921037916122147
맛있댜/Noun 0.31181674718077157
ㅈㅁㅌㅌㄱㄹ/KoreanParticle 0.31181674718077157
흠/Noun 0.22722099938708767
하/Suffix 0.22499528665817484
비싸다으/Adjective 0.22048773641905486
맛잇으느/Noun 0.22048773641905486
녹아/Verb 0.22048773641905486
녹는다/Verb 0.22048773641905486
탕/Noun 0.2164660184839546
도리/Noun 0.2164660184839546
아이스크림/Noun 0.21489782468607826
후식/Noun 0.2005066237398065
매번/Noun 0.19501461266793108
맛있어용/Adjective 0.19425368225749348
만족스러/Adjective 0.19425368225749348
삼겹/Noun 0.19379228613545138
떡/Noun 0.19269205269234155
맛있네요/Adjective 0.19081949184719
닭갈비/Noun 0.1884669938903386

 

for coef in coef_pos_index[-20:] :
    print(invert_index_vectorizer[coef[1]], coef[0])
    
할말은/Verb -0.24837542600818435
않습니다/Verb -0.24837542600818435
많지만/Adjective -0.24837542600818435
그냥/Noun -0.2558545368223156
내/Noun -0.2666707017208134
먹기/Noun -0.2691943240304982
불친절해요/Adjective -0.282816560343047
해줌/Verb -0.2946023898159785
편하게/Adjective -0.2946023898159785
ㅜㅜ/KoreanParticle -0.29915590661697383
요/Josa -0.30167857441633256
평범함/Adjective -0.32572092898704597
무질/Noun -0.3292976179312531
너/Modifier -0.3292976179312531
겨/Noun -0.3292976179312531
하지/Verb -0.3303223649310512
비싸긴한데/Adjective -0.35074686047120107
괜찮아요/Adjective -0.35074686047120107
을/Josa -0.3756982096297823
너무/Adverb -0.3907079128326151

 

# 명사 기준으로 긍정 10개, 부정 10개
noun_list=[]
for coef in coef_pos_index :
    category = invert_index_vectorizer[coef[1]].split("/")[1] # 이름 가져오고 split
    if category == 'Noun' :
        noun_list.append((invert_index_vectorizer[coef[1]], coef[0]))
noun_list[:10]

[('맛있댜/Noun', 0.31181674718077157),
 ('흠/Noun', 0.22722099938708767),
 ('맛잇으느/Noun', 0.22048773641905486),
 ('탕/Noun', 0.2164660184839546),
 ('도리/Noun', 0.2164660184839546),
 ('아이스크림/Noun', 0.21489782468607826),
 ('후식/Noun', 0.2005066237398065),
 ('매번/Noun', 0.19501461266793108),
 ('삼겹/Noun', 0.19379228613545138),
 ('떡/Noun', 0.19269205269234155)]

 

# 형용사 기준으로 긍정 10개, 부정 10개
adjective_list=[]
for coef in coef_pos_index :
    category = invert_index_vectorizer[coef[1]].split("/")[1] # 이름 가져오고 split
    if category == 'Adjective' :
        adjective_list.append((invert_index_vectorizer[coef[1]], coef[0]))
adjective_list[:10]      

[('맛있어요/Adjective', 0.31921037916122147),
 ('비싸다으/Adjective', 0.22048773641905486),
 ('맛있어용/Adjective', 0.19425368225749348),
 ('만족스러/Adjective', 0.19425368225749348),
 ('맛있네요/Adjective', 0.19081949184719),
 ('맛있고/Adjective', 0.16450683695840304),
 ('맛있게/Adjective', 0.16330050009866345),
 ('좋음/Adjective', 0.1431229621617376),
 ('정갈하게/Adjective', 0.1431229621617376),
 ('비싸지만/Adjective', 0.1431229621617376)]

 

반응형

+ Recent posts