도찐개찐

[머신러닝-마이닝] 05. 텍스트 마이닝 본문

PYTHON/데이터분석

[머신러닝-마이닝] 05. 텍스트 마이닝

도개진 2023. 1. 4. 10:48

텍스트 마이닝

  • 자연어로 구성된 "비정형 데이터"에서 패턴 또는 관계를 추출하여 의미 있는 정보를 찾아내는 기법들을 의미
  • 트위터, 페이스북, 블로그, 웹게시판, 온라인 뉴스등 웹 상에 존재하는 방대한 비정형 데이터를 실시간으로 수집/분석하여
  • 고객의 감성 및 의도등을 알아내는 과정을 의미
    • 자연어 처리(Natural Language Processing)와 텍스트 분석(Text Analytics)등이 핵심 분야

텍스트 마이닝 활용분야

  • 텍스트분류 : 문서가 특정 분류/카테고리에 속하는 것을 예측
  • 감성분석 : 텍스트에 나타나는 감정/판단/믿음/의견등 주관적인 요소를 분석
  • 텍스트요약 : 텍스트 내에서 중요한 주제나 중심사상을 추출
  • 텍스트군집화 : 비슷한 유형의 문서에 대해 군집화를 수행

텍스트 분석 수행 과정

  • 텍스트 사전준비/전처리
    • 대/소문자변경, 문장/숫자/특수문자 삭제, 토큰화, 불용어 제거, 어근추출
  • 특성 벡터화/추출
    • 가공된 텍스트에서 특성 추출후 여기에 벡터값을 지정, BOW, Word2Vec등을 사용
  • 머신러닝모델 학습/예측/평가 :
    • 감성분석/연관성분석

파이썬 기반 텍스트 분석 패키지

  • NLTK, KoNLPy : 영어권/한국어 형태소 분석기
  • Gensim : 텍스트요약(토픽모델링)에 두각을 나타냄, word2vec 기능도 구현되어 있음
  • SpaCy : 최근 주목받는 NLP 패키지
  • NLTK : 교육용으로 개발된 자연어 처리 및 분석용 파이썬 패키지, 다양한 예제를 포함하고 있음
# !conda install -c conda-forge -y nltk
!pip install nltk
Requirement already satisfied: nltk in /home/bigdata/.py39/lib/python3.9/site-packages (3.7)
Requirement already satisfied: click in /home/bigdata/.py39/lib/python3.9/site-packages (from nltk) (8.1.3)
Requirement already satisfied: regex>=2021.8.3 in /home/bigdata/.py39/lib/python3.9/site-packages (from nltk) (2022.10.31)
Requirement already satisfied: tqdm in /home/bigdata/.py39/lib/python3.9/site-packages (from nltk) (4.64.1)
Requirement already satisfied: joblib in /home/bigdata/.py39/lib/python3.9/site-packages (from nltk) (1.1.1)
import nltk
# nltk 내장 영어 말뭉치 corpus
nltk.download('book', quiet=True)
True
from nltk.book import *
# text1 부터 text9 까지 다양한 텍스트가 제공
# sents1 부터 sents2 까지 다양한 문장이 제공
*** Introductory Examples for the NLTK Book ***
Loading text1, ..., text9 and sent1, ..., sent9
Type the name of the text or sentence to view it.
Type: 'texts()' or 'sents()' to list the materials.
text1: Moby Dick by Herman Melville 1851
text2: Sense and Sensibility by Jane Austen 1811
text3: The Book of Genesis
text4: Inaugural Address Corpus
text5: Chat Corpus
text6: Monty Python and the Holy Grail
text7: Wall Street Journal
text8: Personals Corpus
text9: The Man Who Was Thursday by G . K . Chesterton 1908
sents()
sent1: Call me Ishmael .
sent2: The family of Dashwood had long been settled in Sussex .
sent3: In the beginning God created the heaven and the earth .
sent4: Fellow - Citizens of the Senate and of the House of Representatives :
sent5: I have a problem with people PMing me to lol JOIN
sent6: SCENE 1 : [ wind ] [ clop clop clop ] KING ARTHUR : Whoa there !
sent7: Pierre Vinken , 61 years old , will join the board as a nonexecutive director Nov. 29 .
sent8: 25 SEXY MALE , seeks attrac older single lady , for discreet encounters .
sent9: THE suburb of Saffron Park lay on the sunset side of London , as red and ragged as a cloud of sunset .
text1[:50]
['[',
 'Moby',
 'Dick',
 'by',
 'Herman',
 'Melville',
 '1851',
 ']',
 'ETYMOLOGY',
 '.',
 '(',
 'Supplied',
 'by',
 'a',
 'Late',
 'Consumptive',
 'Usher',
 'to',
 'a',
 'Grammar',
 'School',
 ')',
 'The',
 'pale',
 'Usher',
 '--',
 'threadbare',
 'in',
 'coat',
 ',',
 'heart',
 ',',
 'body',
 ',',
 'and',
 'brain',
 ';',
 'I',
 'see',
 'him',
 'now',
 '.',
 'He',
 'was',
 'ever',
 'dusting',
 'his',
 'old',
 'lexicons',
 'and']
# 저작권이 말소된 문학작품을 포함하는
# 구텐베르그 말뭉치를 확인
nltk.corpus.gutenberg.fileids()
['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']
# '햄릿'을 불러서 출력해봄
doc = nltk.corpus.gutenberg.raw('shakespeare-hamlet.txt')
print(doc[:300])
[The Tragedie of Hamlet by William Shakespeare 1599]


Actus Primus. Scoena Prima.

Enter Barnardo and Francisco two Centinels.

  Barnardo. Who's there?
  Fran. Nay answer me: Stand & vnfold
your selfe

   Bar. Long liue the King

   Fran. Barnardo?
  Bar. He

   Fran. You come most carefully vpon 

텍스트 토큰화

  • 문장 토큰화 : 문서에서 문장을 분리
  • 단어 토큰화 : 문장에서 단어를 분리
# 행태소 분석을 위한 패키지 적재
from nltk import sent_tokenize
from nltk import word_tokenize
nltk.download('punkt')
[nltk_data] Downloading package punkt to /home/bigdata/nltk_data...
[nltk_data]   Package punkt is already up-to-date!





True
text_sample = 'The Matrix is everywhere its all around us, here even in this room. \
               You can see it out your window or on your television. \
               You feel it when you go to work, or go to church or pay your taxes.'
# 문장토큰화 : sent_tokenize
# 문장토큰화 결과는 리스트로 생성
import re
sents = sent_tokenize(text_sample)
for i in range(len(sents)):
    sents[i] = text_sample = re.sub('[^a-zA-Z ]', '', sents[i])
sents
['The Matrix is everywhere its all around us here even in this room',
 'You can see it out your window or on your television',
 'You feel it when you go to work or go to church or pay your taxes']
# 단어토큰화 : word_tokenize
# 단어토큰화 결과는 리스트로 생성
words = word_tokenize(sents[0])
words
['The',
 'Matrix',
 'is',
 'everywhere',
 'its',
 'all',
 'around',
 'us',
 'here',
 'even',
 'in',
 'this',
 'room']

불용어stopword 제거

  • 분석상 큰 의미가 없는 단어를 지칭
  • NLTK상에서는 stopwords 형태로 제공됨, 단 단어수는 적음
nltk.download('stopwords')
[nltk_data] Downloading package stopwords to
[nltk_data]     /home/bigdata/nltk_data...
[nltk_data]   Package stopwords is already up-to-date!





True
nltk.corpus.stopwords.words('english')[:10]
['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're"]
# 불용어를 이용한 단어토큰화 실시
stopwords = nltk.corpus.stopwords.words('english')
tokens = []
# stopwords
for w in words:
    if w.lower() not in stopwords: 
        tokens.append(w.lower())
tokens
['matrix', 'everywhere', 'around', 'us', 'even', 'room']
import requests

url = 'https://raw.githubusercontent.com/siestageek/datasets/master/txt/thrump.txt'

res = requests.get(url)
docs = res.text
docs[:100]
'Assembly Speaker Chung, distinguished members of this Assembly, ladies and gentlemen: Thank you for '
sents = sent_tokenize(docs)
words = [word_tokenize(re.sub('[^a-zA-Z\s]', '', s)) for s in sents]
# words
# for i in range(len(sents)):
#     sents[i] = text_sample = re.sub('[^a-zA-Z\s]', '', sents[i])
#     words.append(word_tokenize(sents[i]))
tokens = []
# stopwords
for w in words:
    token_line = []
    for w_sub in w:
        if w_sub.lower() not in stopwords: 
            tokens.append(w_sub.lower())
#         if w_sub.lower() not in stopwords: 
#             token_line.append(w_sub.lower())

#     tokens.append(token_line)
import pandas as pd
df = pd.DataFrame(tokens)
df.value_counts()
# tokens = set(tokens)
# tokens = list(tokens)
# tokens
north     34
korea     34
korean    33
people    24
south     17
          ..
gold       1
golf       1
golfer     1
good       1
youve      1
Length: 1030, dtype: int64

스티브잡스 스탠포드대 졸업 축사문에 대한 단어 토큰화

url = 'https://raw.githubusercontent.com/siestageek/datasets/master/txt/stevejobs_en.txt'

res = requests.get(url)
docs = res.text
sents = sent_tokenize(docs)
words2 = [word_tokenize(re.sub('[^a-zA-Z\s]', '', s)) for s in sents]

# tokens2 = [w_sub.lower() for w in words for w_sub in w if w_sub.lower() not in stopwords]
# tokens2
tokens2 = []
for w in words:
    token_list = []
    for w_sub in w:
        if w_sub.lower() not in stopwords: 
            # tokens2.append(w_sub.lower())
            token_list.append(w_sub.lower())
    tokens2.append(token_list)
# import pandas as pd
# df = pd.DataFrame(tokens2)
# df.value_counts()

형태소 분석

  • 형태소는 언어학에서 일정한 의미가 있는 가장 작은 말의 단위를 의미
  • 주어진 단어 또는 어절을 구성하는 각 형태소를 분리한 후
  • 분리된 형태소의 기본형 및 품사 정보를 추출하는 것을 형태소 분석이라 함
  • 문장 > 어절 > 단어 > 어근/접두사/접미사 > 품사
  • 필요에 따라 어근 추출 또는 원형 복원을 하기도 함

품사 태깅POS tagging

  • 단어를 문법적 기능이나 형태, 의미에 따라 구분한 것
  • NLTK에서는 Penn Treebank Tagset을 이용해서 품사태깅을 함
    • NNP고유명사, VB동사, VBP현재진행형 동사, TO전치사, NN명사, DT관형사, ADJ형용사
nltk.download('averaged_perceptron_tagger')
[nltk_data] Downloading package averaged_perceptron_tagger to
[nltk_data]     /home/bigdata/nltk_data...
[nltk_data]   Package averaged_perceptron_tagger is already up-to-
[nltk_data]       date!





True
from nltk.tag import pos_tag
text = 'You feel it when you go to work, or go to church or pay your taxes.'

text = re.sub('[^a-zA-Z\s]','', text)  # 특수기호,숫자 제거
words = word_tokenize(text)

tokens = []
for w in words:                      # 불용어 제거
    w = w.lower()
    if w not in stopwords:    
        tokens.append(w)

print(words)
print(tokens)
['You', 'feel', 'it', 'when', 'you', 'go', 'to', 'work', 'or', 'go', 'to', 'church', 'or', 'pay', 'your', 'taxes']
['feel', 'go', 'work', 'go', 'church', 'pay', 'taxes']
# 품사태깅 : pos_tag
tags = pos_tag(tokens)
tags
[('feel', 'NN'),
 ('go', 'VB'),
 ('work', 'NN'),
 ('go', 'VBP'),
 ('church', 'JJ'),
 ('pay', 'NN'),
 ('taxes', 'NNS')]

문서에서 명사만 추출하기

NN = [w[0] for w in tags if w[1] == 'NN']
NN
['feel', 'work', 'pay']
# 매트릭스 대사에서 명사만 추출
text_sample = 'The Matrix is everywhere its all around us, here even in this room. \
               You can see it out your window or on your television. \
               You feel it when you go to work, or go to church or pay your taxes.'

text_sample = re.sub('[^a-zA-Z\s]','', text_sample)  # 특수기호,숫자 제거
words = word_tokenize(text_sample)

tokens = []
for w in words:                      # 불용어 제거
    w = w.lower()
    if w not in stopwords:    
        tokens.append(w)

print(words)
print(tokens)
['The', 'Matrix', 'is', 'everywhere', 'its', 'all', 'around', 'us', 'here', 'even', 'in', 'this', 'room', 'You', 'can', 'see', 'it', 'out', 'your', 'window', 'or', 'on', 'your', 'television', 'You', 'feel', 'it', 'when', 'you', 'go', 'to', 'work', 'or', 'go', 'to', 'church', 'or', 'pay', 'your', 'taxes']
['matrix', 'everywhere', 'around', 'us', 'even', 'room', 'see', 'window', 'television', 'feel', 'go', 'work', 'go', 'church', 'pay', 'taxes']
# 품사태깅 : pos_tag
tags = pos_tag(tokens)
tags

NN = [w[0] for w in tags if w[1] == 'NN']
NN
['matrix', 'room', 'see', 'television', 'feel', 'work', 'pay']
# 트럼프 연설문에서 명사만 추출하기
url = 'https://raw.githubusercontent.com/siestageek/datasets/master/txt/thrump.txt'

res = requests.get(url)
docs = res.text

docs = re.sub('[^a-zA-Z\s]','', docs)  # 특수기호,숫자 제거
words = word_tokenize(docs)

tokens = []
for w in words:                      # 불용어 제거
    w = w.lower()
    if w not in stopwords:    
        tokens.append(w)

# print(words)
# print(tokens)

# 품사태깅 : pos_tag
tags = pos_tag(tokens)

NN = [w[0] for w in tags if w[1] == 'NN']
# 품사태깅 : pos_tag
url = 'https://raw.githubusercontent.com/siestageek/datasets/master/txt/stevejobs_en.txt'

res = requests.get(url)
docs = res.text

docs = re.sub('[^a-zA-Z\s]','', docs)  # 특수기호,숫자 제거
words = word_tokenize(docs)

tokens = []
for w in words:                      # 불용어 제거
    w = w.lower()
    if w not in stopwords:    
        tokens.append(w)

tags0 = pos_tag(tokens)

NN0 = [w[0] for w in tags if w[1] == 'NN']

단어 빈도

  • 텍스트 데이터의 대표단어를 알아볼수 있는 방법
  • 단어별 빈도를 조사해서 알아낸 상위 n개 단어들을 대표단어로 지정
from nltk import FreqDist

# 각 단어별 빈도 출력
NN0 = []
for ns in tags0: # 각 문장별로 추출된 명사를 하나로 합침
    for n in ns:
        NN0.append(n)

fdwords = FreqDist(NN0)
fdwords
FreqDist({'NN': 314, 'JJ': 185, 'RB': 95, 'VBD': 94, 'NNS': 92, 'VBP': 52, 'VBG': 39, 'VBN': 38, 'VB': 36, 'CD': 17, ...})
# FreqDist 좀 더 자세히 알아보기
# print(fdwords.N())       # 전체 단어수
# print(fdwords['room'])   # 특정 단어 빈도수
# print(fdwords.most_common())   # 빈도가 높은 단어순으로 출력
print(fdwords.most_common(3))  # 빈도가 높은순으로 n개의 단어 출력
[('NN', 314), ('JJ', 185), ('RB', 95)]

트럼프 연설문에서 가장 많이 언급 된 15개 단어

url = 'https://raw.githubusercontent.com/siestageek/datasets/master/txt/thrump.txt'

res = requests.get(url)
docs = res.text
sents = sent_tokenize(docs)
words = [word_tokenize(re.sub('[^a-zA-Z\s]', '', s)) for s in sents]
# words
# for i in range(len(sents)):
#     sents[i] = text_sample = re.sub('[^a-zA-Z\s]', '', sents[i])
#     words.append(word_tokenize(sents[i]))
trump_tokens = []
# stopwords
for w in words:
    token_line = []
    for w_sub in w:
        if w_sub.lower() not in stopwords: 
            trump_tokens.append(w_sub.lower())
#         if w_sub.lower() not in stopwords: 
#             token_line.append(w_sub.lower())

#     tokens.append(token_line)

# tokens = set(tokens)
# tokens = list(tokens)
# tokens
tags = pos_tag(trump_tokens)
word_tags = []
for ns in tags: # 각 문장별로 추출된 명사를 하나로 합침
    for n in ns:
        word_tags.append(n)

fdwords = FreqDist(trump_tokens)
fdwords.most_common(15)
# NN0 = [w[0] for w in tags if w[1] == 'NN']
[('korea', 34),
 ('north', 34),
 ('korean', 33),
 ('people', 24),
 ('south', 17),
 ('regime', 17),
 ('nations', 13),
 ('world', 13),
 ('one', 12),
 ('united', 11),
 ('states', 11),
 ('today', 10),
 ('peninsula', 10),
 ('nation', 9),
 ('nuclear', 9)]
url = 'https://raw.githubusercontent.com/siestageek/datasets/master/txt/stevejobs_en.txt'

res = requests.get(url)
docs = res.text
sents = sent_tokenize(docs)
words = [word_tokenize(re.sub('[^a-zA-Z\s]', '', s)) for s in sents]
# words
# for i in range(len(sents)):
#     sents[i] = text_sample = re.sub('[^a-zA-Z\s]', '', sents[i])
#     words.append(word_tokenize(sents[i]))
steve_tokens = []
# stopwords
for w in words:
    token_line = []
    for w_sub in w:
        if w_sub.lower() not in stopwords: 
            steve_tokens.append(w_sub.lower())
#         if w_sub.lower() not in stopwords: 
#             token_line.append(w_sub.lower())

#     tokens.append(token_line)
# tokens = set(tokens)
# tokens = list(tokens)
# tokens
tags = pos_tag(steve_tokens)
word_tags = []
for ns in tags: # 각 문장별로 추출된 명사를 하나로 합침
    for n in ns:
        word_tags.append(n)

fdwords = FreqDist(steve_tokens)
fdwords.most_common(15)
# NN0 = [w[0] for w in tags if w[1] == 'NN']
[('life', 16),
 ('college', 14),
 ('one', 9),
 ('would', 9),
 ('years', 9),
 ('never', 8),
 ('apple', 8),
 ('dropped', 7),
 ('months', 7),
 ('later', 7),
 ('looking', 7),
 ('dont', 7),
 ('want', 6),
 ('everything', 6),
 ('didnt', 6)]

워드클라우드wordcloud

  • 문서에 사용된 단어의 중요도나 인기도 등을 고려해서
  • 시각적으로 늘어 놓아 표시하는 시각화 유형을 의미
  • 파이썬용 워드클라우드 패키지
!pip install wordcloud
Collecting wordcloud
  Downloading wordcloud-1.8.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (458 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 458.6/458.6 kB 26.7 MB/s eta 0:00:00
[?25hRequirement already satisfied: pillow in /home/bigdata/.py39/lib/python3.9/site-packages (from wordcloud) (9.3.0)
Requirement already satisfied: matplotlib in /home/bigdata/.py39/lib/python3.9/site-packages (from wordcloud) (3.6.2)
Requirement already satisfied: numpy>=1.6.1 in /home/bigdata/.py39/lib/python3.9/site-packages (from wordcloud) (1.23.4)
Requirement already satisfied: fonttools>=4.22.0 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (4.38.0)
Requirement already satisfied: packaging>=20.0 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (21.3)
Requirement already satisfied: kiwisolver>=1.0.1 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (1.4.4)
Requirement already satisfied: cycler>=0.10 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (0.11.0)
Requirement already satisfied: pyparsing>=2.2.1 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (3.0.9)
Requirement already satisfied: contourpy>=1.0.1 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (1.0.6)
Requirement already satisfied: python-dateutil>=2.7 in /home/bigdata/.py39/lib/python3.9/site-packages (from matplotlib->wordcloud) (2.8.2)
Requirement already satisfied: six>=1.5 in /home/bigdata/.py39/lib/python3.9/site-packages (from python-dateutil>=2.7->matplotlib->wordcloud) (1.16.0)
Installing collected packages: wordcloud
Successfully installed wordcloud-1.8.2.2
from wordcloud import WordCloud
import matplotlib.pyplot as plt
from collections import Counter
# generate_from_text : 텍스트 데이터로 부터 빈도를 계산해 시각화
wc = WordCloud(background_color='white',
                width=640, height=480).generate_from_text(' '.join(trump_tokens))
plt.imshow(wc, interpolation='bilinear')
plt.axis('off')
plt.show()

# generate_from_frequencies : 빈도가 dict형태로 처리 된 데이터로 시각화
steve_mc = dict(FreqDist(steve_tokens).most_common(15))

wc = WordCloud(background_color='red',
                width=640, height=480 ).generate_from_frequencies(FreqDist(steve_tokens))
plt.imshow(wc, interpolation='bilinear')
plt.axis('off')
plt.show()

import numpy as np
from PIL import Image

mask_path = '../img/crab.png'       # 투명성 없는 파일
mask = np.array(Image.open(mask_path))   # 배경 이미지 파일을 numpy 배열로 변환

wcimg = WordCloud(background_color='white',
        mask=mask, stopwords=stopwords, width=640, height=480).generate_from_text(' '.join(steve_tokens))
plt.imshow(wcimg, interpolation='bilinear')
plt.axis('off')
plt.show()      

728x90
Comments