텍스트를 전처리하고 단어빈도그래프와 다양한 형태의 워드클라우드로 나타내보자.
그래프를 그리기전, 문서들로부터 각 단어들을 분리하는 텍스트 전처리를 거쳐야한다.
(토큰화, 어간추출, 불용어) 등 이용
실습할 문서
import nltk
nltk.download("gutenberg")
from nltk.corpus import gutenberg
file_names = gutenberg.fileids()
print(file_names)
>>>
['austen-emma.txt', 'austen-persuasion.txt', 'austen-sense.txt', 'bible-kjv.txt', 'blake-poems.txt', 'bryant-stories.txt', 'burgess-busterbrown.txt', 'carroll-alice.txt', 'chesterton-ball.txt', 'chesterton-brown.txt', 'chesterton-thursday.txt', 'edgeworth-parents.txt', 'melville-moby_dick.txt', 'milton-paradise.txt', 'shakespeare-caesar.txt', 'shakespeare-hamlet.txt', 'shakespeare-macbeth.txt', 'whitman-leaves.txt']
책들을 다운받고 책 제목을 확인해보는 코드
auseten이라는 책의 텍스트를 이용해 진행!
doc_auseten = gutenberg.open("austen-persuasion.txt").read()
print("#Num of charaters used: ", len(doc_auseten))
print("#Text sample:")
print(doc_auseten[:500])
>>>
#Num of charaters used: 466292
#Text sample:
[Persuasion by Jane Austen 1818]
Chapter 1
Sir Walter Elliot, of Kellynch Hall, in Somersetshire, was a man who,
for his own amusement, never took up any book but the Baronetage;
there he found occupation for an idle hour, and consolation in a
distressed one; there his faculties were roused into admiration and
respect, by contemplating the limited remnant of the earliest patents;
there any unwelcome sensations, arising from domestic affairs
changed naturally into pity and contempt as he turn
from nltk.tokenize import word_tokenize
tokens_auseten = word_tokenize(doc_auseten)
print("#Num of tokens used")
print("#Tokens sample")
print(tokens_auseten[:20])
>>> #Num of tokens used 97918
#Tokens sample:
['[', 'Persuasion', 'by', 'Jane', 'Austen', '1818', ']', 'Chapter', '1', 'Sir', 'Walter', 'Elliot', ',', 'of', 'Kellynch', 'Hall', ',', 'in', 'Somersetshire', ',']
auseten에 대한 단어를 토큰화하고 토큰 수와 앞 20개의 토큰 확인
stemming = 어간 추출
from nltk.stem import PorterStemmer
stemmer = PorterStemmer()
# 모든 토큰에 대해 스테밍 실행
stem_tokens_auseten = [stemmer.stem(token) for token in tokens_auseten ]
# 모든 tokens_auseten에 들어있는 token을 반복문으로 가져와서 stemmer.stem에 넣어 스태밍을 실시한다.
print('#Num of tokens after stemmming: ', len(stem_tokens_auseten))
print("#Token Sample: ")
print(stem_tokens_auseten[:20])
>>> #Num of tokens after stemmming: 97918
#Token Sample:
['[', 'persuas', 'by', 'jane', 'austen', '1818', ']', 'chapter', '1', 'sir', 'walter', 'elliot', ',', 'of', 'kellynch', 'hall', ',', 'in', 'somersetshir', ',']
그 결과 , 나온 결과 값의 예를 들자면
['[', 'Persuasion', 'by', 'Jane', 'Austen', '1818', ']', 'Chapter', '1', 'Sir', 'Walter', 'Elliot', ',', 'of', 'Kellynch', 'Hall', ',', 'in', 'Somersetshire', ',']
→
['[', 'persuas', 'by', 'jane', 'austen', '1818', ']', 'chapter', '1', 'sir', 'walter', 'elliot', ',', 'of', 'kellynch', 'hall', ',', 'in', 'somersetshir', ',']
이런식으로 변환이 됨을 볼 수 있다.
WordNetLemmatizer 는 입력으로 단어가 동사, 품사라는 사실을 알려줄 수 있음
ex) dies , watched, has 가 문장에서 동사로 쓰였다는 것을 알려준다면 표제어 추출기는 품사의 정보를 보존하면서 정확한 Lemma를 출력하게 됩니다.
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
lem_tokens_auseten = [ lemmatizer.lemmatize(token) for token in tokens_auseten ]
print('#Num of tokens after stemmming: ', len(stem_tokens_auseten))
print("#Token Sample: ")
print(stem_tokens_auseten[:20])
>>> #Num of tokens after stemmming: 97918
#Token Sample:
['[', 'persuas', 'by', 'jane', 'austen', '1818', ']', 'chapter', '1', 'sir', 'walter', 'elliot', ',', 'of', 'kellynch', 'hall', ',', 'in', 'somersetshir', ',']
💡 결과를 종합적으로 확인했을때, 토큰 수는 변화 없다!
why? 토큰화된 결과에 대해 개별적으로 어간 추출과 표제어 추출을 수행하기 때문이다.
정규표현식을 이용해 토큰화를 하고 결과를 비교해보자
from nltk.tokenize import RegexpTokenizer
tokenizer = RegexpTokenizer("[\w']{3,}")
reg_tokens_auseten = tokenizer.tokenize(doc_auseten.lower())
# print(reg_tokens_auseten)
print('#Num of tokens with RegexpTokenizer: ', len(reg_tokens_auseten))
print("#Token Sample: ")
print(reg_tokens_auseten[:20])
>>> #Num of tokens with RegexpTokenizer: 65707
#Token Sample:
['persuasion', 'jane', 'austen', '1818', 'chapter', 'sir', 'walter', 'elliot', 'kellynch', 'hall', 'somersetshire', 'was', 'man', 'who', 'for', 'his', 'own', 'amusement', 'never', 'took']
wordTokenizer을 사용했을때보다 토큰 수가 현저히 줄어들었음을 확인 할 수 잇다.
‘[’]의 부호와 2자 이하의 글자들이 모두 제외되었기 때문이다.
여기서 생각해봐야할것 : 우리가 진행할 목적이 그래프를 이용한 시각화이기 때문에 단어를 알아보기 쉽게 시각화시켜야 한다. 따라서 stemming은 사용하지 않도록 !!!
from nltk.corpus import stopwords
english_stops = set(stopwords.words('english')) # 반복되지 않도록 set로 변환 -> 자동 중복 제거
# stopwords를 제외한 단어들만으로 리스트를 생성하기
result_auseten = [ word for word in reg_tokens_auseten if word not in english_stops ]
print('#Num of tokens after stopword elimination : ', len(result_auseten))
print("#Token Sample: ")
print(result_auseten[:20])
>>> #Num of tokens after stopword elimination : 37883
#Token Sample:
['persuasion', 'jane', 'austen', '1818', 'chapter', 'sir', 'walter', 'elliot', 'kellynch', 'hall', 'somersetshire', 'man', 'amusement', 'never', 'took', 'book', 'baronetage', 'found', 'occupation', 'idle']
불용어 제거 후, 단어가 더 감소함을 확인할 수 있다.
auseten_word_count = dict()
for word in result_auseten:
auseten_word_count[word] = auseten_word_count.get(word,0)+1
print('#Num of use words : ', len(auseten_word_count))
sorted_word_count = sorted(auseten_word_count, key=auseten_word_count.get, reverse = True)
print("#Top 20 high frequency: ")
for key in sorted_word_count[:20]:
print(f'{repr(key)}: {auseten_word_count[key]}', end=",")
auseten_word_count 의 dict객체 생성
result_auseten 안의 단어들을 받고 딕셔너리에 저장
딕셔너리로 단어별 개수를 세고, 빈도가 큰 순으로 정리하기
빈도수 상위 단어를 봤을때, could와 같은 딱히 필요없는 조동사와 같은 단어가 있음… → 품사 태깅을 진행하는 것이 필요할 것으로 보임!
my_tag_set = ["NN","VB", "VBD","JJ"]
my_words = [word for word, tag in nltk.pos_tag(result_auseten) if tag in my_tag_set]
auseten_word_count = dict()
for word in my_words:
auseten_word_count[word] = auseten_word_count.get(word,0)+1
# 정렬하기
sorted_word_count = sorted(auseten_word_count, key=auseten_word_count.get, reverse = True)
# 빈도수 상위 20개의 단어를 출력
for key in sorted_word_count[:20]:
print(f'{repr(key)}: {auseten_word_count[key]}', end=",")
>>>'anne': 327,'captain': 232,'mrs': 230,'elliot': 205,'lady': 202,'good': 187,'wentworth': 176,'said': 173,'little': 168,'time': 152,'nothing': 139,'sir': 135,'great': 130,'man': 127,'walter': 117,'much': 117,'mary': 111,'miss': 109,'house': 94,'last': 88,
각각 NN, VB, VBD, JJ 의 태크 셋을 형성하고 단어 태깅하기
정렬후 단어를 출력하고 matplotlib.pyplot을 이용해서 그래프를 그린다 !
import matplotlib.pyplot as plt
%matplotlib inline
# 정렬된 단어 리스트에 대해 빈도수를 가져와서 리스트 생성
w = [auseten_word_count[key] for key in sorted_word_count]
plt.plot(w)
plt.show()

출력 결과
지프의 법칙이 보임
n = sorted_word_count[:20][::-1]
# print(type(sorted_word_count))
# 20개의 단어에 대한 빈도수 체크
w = [auseten_word_count[key] for key in n]
plt.barh(range(len(n)),w,tick_label = n)
plt.show()

기본 install
!pip install wordcloud
from wordcloud import WordCloud
wordcloud = WordCloud().generate(doc_auseten)
plt.axis("off")
plt.imshow(wordcloud, interpolation='bilinear')
plt.show()

텍스트 데이터를 전처리한 워드클라우드 완성
wordcloud = WordCloud(max_font_size = 60).generate_from_frequencies(auseten_word_count)
plt.figure()
plt.axis("off")
plt.imshow(wordcloud, interpolation = "bilinear")
plt.show()

import numpy as np
from PIL import Image
alice_mask = np.array(Image.open("alice_mask.png"))
wc = WordCloud(background_color="white",
max_words=30,
mask=alice_mask,
contour_width=3,
contour_color='steelblue')
wc.generate_from_frequencies(auseten_word_count)
wc.to_file("alice.png")
# 결과 그리기
plt.figure()
plt.axis("off")
plt.imshow(wc, interpolation='bilinear')
plt.show()

< konlpy install >
!apt-get update !apt-get install g++ openjdk-8-jdk !pip install konlpy JPype1-py3 !bash <(curl -s https://raw.githubusercontent.com/konlpy/konlpy/master/scripts/mecab.sh)
from konlpy.corpus import kolaw
const_doc = kolaw.open('constitution.txt').read()
print(type(const_doc))
print(len(const_doc))
print(const_doc[:600])
>>>
<class 'str'>
18884
대한민국헌법
유구한 역사와 전통에 빛나는 우리 대한국민은 3·1운동으로 건립된 대한민국임시정부의 법통과 불의에 항거한 4·19민주이념을 계승하고, 조국의 민주개혁과 평화적 통일의 사명에 입각하여 정의·인도와 동포애로써 민족의 단결을 공고히 하고, 모든 사회적 폐습과 불의를 타파하며, 자율과 조화를 바탕으로 자유민주적 기본질서를 더욱 확고히 하여 정치·경제·사회·문화의 모든 영역에 있어서 각인의 기회를 균등히 하고, 능력을 최고도로 발휘하게 하며, 자유와 권리에 따르는 책임과 의무를 완수하게 하여, 안으로는 국민생활의 균등한 향상을 기하고 밖으로는 항구적인 세계평화와 인류공영에 이바지함으로써 우리들과 우리들의 자손의 안전과 자유와 행복을 영원히 확보할 것을 다짐하면서 1948년 7월 12일에 제정되고 8차에 걸쳐 개정된 헌법을 이제 국회의 의결을 거쳐 국민투표에 의하여 개정한다.
제1장 총강
제1조 ① 대한민국은 민주공화국이다.
②대한민국의 주권은 국민에게 있고, 모든 권력은 국민으로부터 나온다.
제2조 ① 대한민국의 국민이 되는 요건은 법률로 정한다.
②국가는 법률이 정하는 바에 의하여 재외국민을 보호할 의무를 진다.
제3조 대한민
형태소 단위로 토크나이즈
from konlpy.tag import Okt
t = Okt()
tokens_const = t.morphs(const_doc)
print('#토큰의 수:', len(tokens_const))
print('#앞 100개의 토큰')
print(tokens_const[:100])
>>>
#토큰의 수: 3882
#앞 100개의 토큰
['대한민국', '헌법', '유구', '역사', '전통', '우리', '국민', '운동
형태소 단위로 토크나이즈 후 명사만 추출
tokens_const = t.nouns(const_doc)
print('#토큰의 수:', len(tokens_const))
print('#앞 100개의 토큰')
print(tokens_const[:100])
>>> #토큰의 수: 3882
#앞 100개의 토큰
['대한민국', '헌법', '유구', '역사', '전통', '우리', '국민', '운동'
토큰화할때 길이가 1 이상인 단어들만 저장하기
tokens_const = [token for token in tokens_const if len(token) > 1]
print('#토큰의 수:', len(tokens_const))
print('#앞 100개의 토큰')
print(tokens_const[:100])
>
#토큰의 수: 3013
#앞 100개의 토큰
['대한민국', '헌법', '유구', '역사', '전통', '우리', '국민', '운동
!apt-get update -qq !apt-get install fonts-nanum* -qq FONT_PATH = "/usr/share/fonts/truetype/nanum/NanumGothic.ttf"
나눔 고딕 install 및 폰트 경로 지정하기
from matplotlib import font_manager, rc
import platform
!pip install koreanize-matplotlib
import koreanize_matplotlib
라이브러리 불러오기
font_name = font_manager.FontProperties(fname=FONT_PATH).get_name()
rc('font', family=font_name)
const_cnt = {}
for word in tokens_const:
const_cnt[word] = const_cnt.get(word, 0) + 1
def word_graph(cnt, max_words=10):
sorted_w = sorted(cnt.items(), key=lambda kv: kv[1])
print(sorted_w[-max_words:])
n, w = zip(*sorted_w[-max_words:])
plt.barh(range(len(n)),w,tick_label=n)
plt.show()
word_graph(const_cnt, max_words=20)
결과 빈도수 순서대로 막대그래프 만들기 barh
<한글 그래프에 출력 안돼…. ㅠㅠㅠ>

font_path = FONT_PATH
wordcloud = WordCloud(font_path = font_path).generate(const_doc)
plt.axis("off")
plt.imshow(wordcloud, interpolation='bilinear')
plt.show()
기본 워드 클라우드 생성

내가 원하는 색깔 크기의 워드클라우드 생성
