# import package
import numpy as np
import pandas as pd
import collections, time, os, re
from itertools import chain
from collections import Counter
from nltk import collocations, everygrams, word_tokenize
from tqdm import tnrange, tqdm_notebook
# mecab 형태소분석 명사만
from konlpy.tag import Mecab
mecab = Mecab().nouns
# Twitter 형태소분석 명사만
from konlpy.utils import pprint
from konlpy.tag import Twitter
twitter = Twitter().nouns
# 시각화 라이브러리 한글폰트 설정
from matplotlib import font_manager, rc, pyplot as plt
import seaborn as sns
%matplotlib inline
font_name = font_manager.FontProperties(fname="/usr/share/fonts/truetype/nanum/NanumBarunGothicBold.ttf").get_name()
rc('font', family=font_name)
plt.rcParams["font.family"]
['NanumBarunGothic']
# ngram 설정하기
# 1어절에서 1어절로 세팅
ngram_start = 1
ngram_end = 3
# 히트맵 N x N matrix의 N 설정하기
heatmap_number = 20
# 히트맵 색상 설정
# https://matplotlib.org/users/colormaps.html
colorset = "YlOrRd"
# 형태소분석기 설정 ( mecab , twitter 둘중 하나 선택, mecab이 빠름)
pos_tool = twitter
# pandas로 엑셀 파일 읽기
data = pd.read_excel('Keyword 빈도(Total Data 기준).xlsx', sheetname='Sheet3', header=None)
print("읽은 데이터 행 수 : ", len(data))
data["content"] = data[0].astype(str)
data["content"] = data["content"].str.replace("^"," ")
# 문의글 칼럼의 특수문자를 없애주고 preprocess_question 칼럼에 추가
data['preprocess_question']=[ re.sub('[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&…▶◆\\\=ⓒ\(\'\"]','',i).strip() for i in data["content"] ]
# 2 어절을 추출해서 카운트 하는 방식 (3어절 이상도 가능)
print("ngram_start : ", ngram_start)
print("ngram_end : ", ngram_end)
ngram_data_question = data['preprocess_question'].apply(lambda x: [' '.join(ng) for ng in everygrams(word_tokenize(x), ngram_start, ngram_end)])
# ngram_question 칼럼에 추가
data['ngram_question'] = ngram_data_question
# 형태소 분석 칼럼 추가
data['pos'] = data['preprocess_question'].apply(pos_tool)
def joinlist(list):
return " ".join(list)
# 형태소 분석 결과 한문장으로된 칼럼 추가
data['string_pos'] = data['pos'].apply(joinlist)
# 형태소 분석 명사만 추출 후 2 어절 카운트 (3어절 이상도 가능)
ngram_data_question_pos = data['string_pos'].apply(lambda x: [' '.join(ng) for ng in everygrams(word_tokenize(x), ngram_start, ngram_end)])
# ngram_question 칼럼에 추가
data['ngram_question_pos'] = ngram_data_question_pos
data = data.iloc[:,1:]
data.head(3)
읽은 데이터 행 수 : 9766 ngram_start : 1 ngram_end : 3
content | preprocess_question | ngram_question | pos | string_pos | ngram_question_pos | |
---|---|---|---|---|---|---|
0 | 시각장애인보도블럭 안전보안관 | 시각장애인보도블럭 안전보안관 | [시각장애인보도블럭, 안전보안관, 시각장애인보도블럭 안전보안관] | [시각장애인, 보도, 블럭, 안전, 보안관] | 시각장애인 보도 블럭 안전 보안관 | [시각장애인, 보도, 블럭, 안전, 보안관, 시각장애인 보도, 보도 블럭, 블럭 안... |
1 | 벚꽃나무 안전보안관 | 벚꽃나무 안전보안관 | [벚꽃나무, 안전보안관, 벚꽃나무 안전보안관] | [벚꽃, 나무, 안전, 보안관] | 벚꽃 나무 안전 보안관 | [벚꽃, 나무, 안전, 보안관, 벚꽃 나무, 나무 안전, 안전 보안관, 벚꽃 나무 ... |
2 | 정비 요청 안전보안관 파손도로 파손도 | 정비 요청 안전보안관 파손도로 파손도 | [정비, 요청, 안전보안관, 파손도로, 파손도, 정비 요청, 요청 안전보안관, 안전... | [정비, 요청, 안전, 보안관, 파손, 도로, 파손] | 정비 요청 안전 보안관 파손 도로 파손 | [정비, 요청, 안전, 보안관, 파손, 도로, 파손, 정비 요청, 요청 안전, 안전... |
heatmap_raw_data = data.ngram_question_pos.tolist()
# 카운터 사용
d = Counter(list(chain(*list(heatmap_raw_data))))
df_q = pd.DataFrame.from_dict(d, orient='index').reset_index()
df_q = df_q.sort_values(by=0, ascending=False)
print(ngram_start, "~", ngram_end, "어절 ngram 수", len(df_q))
print(heatmap_number, "x", heatmap_number, "히트맵 설정")
number = df_q['index'][:heatmap_number].tolist()
heatmap_word = []
heatmap_cnt = []
# 히트맵용 카운트
for i in tqdm_notebook(range(len(number))):
heatmap_cnt = []
for j in range(len(number)):
if number[i] == number[j]:
heatmap_cnt.append(0)
break
else:
heatmap_cnt.append(len(data[(data['string_pos'].str.contains(number[i])) & (data['string_pos'].str.contains(number[j]))]))
heatmap_word.append(heatmap_cnt)
# 히트맵용 카운트를 dataframe으로 변경
heatmap_data = pd.DataFrame(heatmap_word, index=number, columns=number)
heatmap_data = heatmap_data.fillna(0)
heatmap_data[heatmap_data.columns] = heatmap_data[heatmap_data.columns].astype(int)
heatmap_data.iloc[:heatmap_number,:heatmap_number]
1 ~ 3 어절 ngram 수 103361 20 x 20 히트맵 설정
안전 | 파손 | 도로 | 차량 | 인도 | 불법 | 사고 | 주차 | 요청 | 통행 | 블럭 | 보수 | 앞 | 설치 | 보도 | 횡단보도 | 공사 | 정비 | 운영 | 보행자 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
안전 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
파손 | 323 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
도로 | 207 | 381 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
차량 | 212 | 169 | 150 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
인도 | 172 | 171 | 38 | 73 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
불법 | 138 | 9 | 47 | 132 | 33 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
사고 | 335 | 155 | 145 | 217 | 63 | 64 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
주차 | 121 | 30 | 59 | 181 | 78 | 221 | 77 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
요청 | 120 | 150 | 119 | 86 | 38 | 114 | 70 | 29 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
통행 | 126 | 89 | 96 | 234 | 102 | 69 | 102 | 86 | 43 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
블럭 | 102 | 191 | 26 | 11 | 170 | 1 | 50 | 10 | 31 | 38 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
보수 | 162 | 146 | 89 | 74 | 66 | 0 | 71 | 14 | 104 | 47 | 42 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
앞 | 132 | 103 | 77 | 76 | 62 | 49 | 58 | 52 | 50 | 34 | 42 | 75 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
설치 | 152 | 32 | 55 | 110 | 52 | 61 | 116 | 46 | 70 | 67 | 9 | 15 | 38 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
보도 | 197 | 247 | 51 | 89 | 115 | 41 | 125 | 63 | 52 | 57 | 453 | 64 | 111 | 68 | 0 | 0 | 0 | 0 | 0 | 0 |
횡단보도 | 88 | 74 | 30 | 80 | 20 | 41 | 70 | 52 | 18 | 30 | 30 | 17 | 74 | 61 | 477 | 0 | 0 | 0 | 0 | 0 |
공사 | 119 | 33 | 57 | 71 | 50 | 17 | 71 | 31 | 29 | 60 | 13 | 43 | 17 | 38 | 23 | 12 | 0 | 0 | 0 | 0 |
정비 | 68 | 136 | 92 | 27 | 37 | 7 | 15 | 9 | 202 | 36 | 36 | 4 | 32 | 12 | 55 | 14 | 9 | 0 | 0 | 0 |
운영 | 3 | 154 | 115 | 0 | 26 | 81 | 0 | 15 | 1 | 2 | 3 | 6 | 1 | 1 | 2 | 0 | 2 | 6 | 0 | 0 |
보행자 | 177 | 79 | 61 | 105 | 58 | 21 | 102 | 34 | 33 | 70 | 58 | 31 | 19 | 57 | 122 | 68 | 24 | 21 | 0 | 0 |
fig, ax = plt.subplots(figsize=(heatmap_number/1.5, heatmap_number/2))
sns.heatmap(heatmap_data.iloc[:heatmap_number,:heatmap_number], cmap=colorset, annot=True, fmt="d", linewidths=.5)
<matplotlib.axes._subplots.AxesSubplot at 0x7f6735d1f588>
# 특정단어를 포함한 ngram만 추출
df_q = df_q.rename(index=str, columns={"index": "ngram", 0: "count"})
# 전체 데이터 저장
df_q.to_csv("ngram.csv")
df_q.head(20)
ngram | count | |
---|---|---|
3 | 안전 | 1934 |
20 | 파손 | 1755 |
21 | 도로 | 1113 |
416 | 차량 | 1061 |
165 | 인도 | 900 |
460 | 불법 | 853 |
96 | 사고 | 816 |
167 | 주차 | 690 |
19 | 요청 | 667 |
471 | 통행 | 637 |
2 | 블럭 | 626 |
73 | 보수 | 609 |
614 | 앞 | 533 |
442 | 설치 | 502 |
1 | 보도 | 496 |
95 | 횡단보도 | 494 |
227 | 공사 | 475 |
18 | 정비 | 475 |
629 | 운영 | 463 |
36 | 보행자 | 454 |
df_q[df_q["ngram"].str.contains("파손")][:20].plot(x='ngram', y='count', kind='barh')
<matplotlib.axes._subplots.AxesSubplot at 0x7f66fd566940>
df_q[df_q["ngram"].str.contains("파손")].head()
ngram | count | |
---|---|---|
20 | 파손 | 1755 |
26 | 도로 파손 | 270 |
2900 | 파손 운영 | 154 |
1668 | 파손 안전 | 125 |
1667 | 블럭 파손 | 116 |
# 파손만 저장
df_q[df_q["ngram"].str.contains("파손")].to_csv("ngram_word.txt")