<jupyter_start><jupyter_text>TEXT MINING for BEGINNER- 본 자료는 텍스트 마이닝을 활용한 연구 및 강의를 위한 목적으로 제작되었습니다.- 본 자료를 강의 목적으로 활용하고자 하시는 경우 꼭 아래 메일주소로 연락주세요.- 본 자료에 대한 허가되지 않은 배포를 금지합니다.- 강의, 저작권, 출판, 특허, 공동저자에 관련해서는 문의 바랍니다.- **Contact : ADMIN(<EMAIL>)**---  DAY 12. 실전 텍스트 분석하기: LDA토픽모델링, 감성분석- Python을 활용해 텍스트 데이터를 분석하는 여러가지 방법에 대해 다룹니다.---  1. LDA토픽모델링: 단어를 문서에 포함된 주제단위로 군집화하기---  1.1. 샘플 데이터 불러오기---<jupyter_code># 기사본문을 저장할 리스트를 생성합니다.
article_list = []

# 기사가 저장된 파일은 data 폴더 안에 저장되어 있습니다.
file_path = "data/article_sample.txt"

# 기사가 저장된 파일을 불러옵니다.
f = open(file_path, encoding="utf-8")
for line in f:
    col = line.strip().split("\t")
    #group = col[0]
    #source = col[1]
    #datetime = col[2]
    #title = col[3]
    # 기사의 본문만 가져와 리스트에 저장합니다.
    content = col[4]
    article_list.append(content)
f.close()

print("총 기사 수 :", len(article_list))
print("기사샘플 :")
print(article_list[3][:1000])<jupyter_output>기사샘플 :
10년 역사 10대 뉴스로 정리(지디넷코리아=임유경 기자)2008년 9월 리먼 브라더스 파산으로 촉발된 금융위기가 전 세계를 뒤흔들었다. 세계 금융시스템이 붕괴 위기에 놓여 있던 이때, 인터넷 세상 한 켠에선 새로운 금융 시스템이 조용하게 작동을 시작했다. 10년 전 1월 3일 오후 6시 15분. 은행이나 정부의 개입이 필요 없는 개인 간 전자화폐 시스템 비트코인 네트워크의 첫 번째 블록이 탄생한 순간이다.이날 사토시 나카모토라는 가명의 인물은 앞서 공개한 논문을 통해 제시한 아이디어를 약 한달만에 구현하는 데 성공했다. 기념비적인 첫 번째 블록에는 "재무장관, 은행에 두 번째 구제금융 제공 임박”이라는 문구를 새겼다. 이날 영국 일간지 <타임스>의 1면 헤드라인으로, 비트코인 네트워크가 기존 금융 시스템에 대안적인 성격을 띤다는 정체성을 분명하게 드러냈다.이후 비트코인은 굴곡진 10년의 세월을 보냈다. 2011년 초까지 1달러의 가치도 인정 받지 못했다. 비트코인에 대한 관심도 암호학 커뮤니티 내 개발자들이 재미로 채굴(트랜잭션을 블록단위로 묶어 처리하는 작업)하고 비트코인을 받는 수준에 그쳤다. 이후 관심을 늘었지만 투기기 불법적인 요소 결합돼 버블이니 검은 돈이니 하는 오명을 얻기도 했다.2009년 1월 3일 비트코인 첫 번째 블록인 제네시스 블록이 생성됐다.여전비 비트코인은 실험대 위에 있다. 가격이 출렁일 때마다 "비트코인은 죽었다"는 제목의 기사가 쏟아져 나온다. 지난해 언론에서 내린 사망선고는 거의 100번에 이른다. (☞관련링크)분명한 것은 비트코인의 잠재력을 믿는 사람들이 늘어나고 있다는 점이다. 이제 전 세계 모든 주요 기업들이 비트코인 기반 기술인 블록체인의 혁신성에 주목하고 기존 비즈니스에 적용하기 위해 연구에 나섰다.비트코인 맥시멀리스트로 알려진 지미 송은 최근 자신의 블로그에 비트코인이 가진 특성 중 탈중앙성(정부나 은행의 개입이 없는 상태)에 주목해야 한다고 강조하며 "기업가들은 비트코인 내에서 자신의 돈과 노력을 가지고[...]<jupyter_text>1.2. 기사 문장을 형태소 단위로 분리하기---<jupyter_code># 꼬꼬마 형태소 분석기를 불러옵니다.
from konlpy.tag import Kkma
kkma = Kkma()

text = "꼬꼬마 형태소 분석기를 불러옵니다."
print(kkma.pos(text))
# 형태소분석 후 기사단위의 형태소 리스트를 저장할 리스트를 생성합니다.
tag_sentence_list = []

# 불용어를 리스트를 정의합니다.
stop_list = ["카페", "카페규정"]

# 형태소 중 꼭 포함해야하는 형태소 태그를 리스트로 생성합니다.
pos_list = ["NNG", "NNP"]

now = 0
for article in article_list:
    now += 1
    print(now, end="\r")
    sentence_list = kkma.sentences(article)
    tag_sentence = []
    for sentence in sentence_list:
        tag_list = kkma.pos(sentence)
        for word, pos in tag_list:
            if pos in pos_list and word not in stop_list and len(word) > 1:
                tag_sentence.append(word)
    tag_sentence_list.append(tag_sentence)<jupyter_output>100<jupyter_text>1.3. LDA토픽모델링 수행하기---<jupyter_code># 텍스트 분석을 위한 다양한 기능을 제공하는 gensim 패키지를 설치합니다.

#!pip install gensim
# gensim 패키지를 불러옵니다.
import gensim
from gensim import corpora

# LDA토픽모델링 수행중에는 진행중인 상황에 대한 로그가 출력됩니다.
# 아래 7~9번째 줄을 주석처리하면 로그를 확인할 수 있습니다.
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.simplefilter(action='ignore', category=RuntimeWarning)
# 추출할 토픽의 개수를 지정합니다.
topic = 3
# 각 토픽에 포함할 키워드(단어) 개수를 지정합니다.
keyword = 10

# LDA토픽모델링을 수행합니다.
dictionary = corpora.Dictionary(tag_sentence_list)    
corpus = [dictionary.doc2bow(text) for text in tag_sentence_list]
ldamodel = gensim.models.ldamodel.LdaModel(corpus, num_topics=topic, id2word = dictionary, passes=10)

# 추출된 토픽과 토픽에 포함된 키워드를 저장할 리스트를 생성합니다.
topic_list = []

# 추출된 토픽을 리스트에 저장합니다.
for num in range(topic):
    topic_list.append(ldamodel.show_topic(num, keyword))
# 추출된 토픽을 출력합니다.
for index in range(len(topic_list)):
    print("# 주제", index + 1)
    for topic in topic_list[index]:
        print("\t", topic[0].split("/")[0])<jupyter_output># 주제 1
	 금리
	 금융
	 대출
	 은행
	 부동산
	 기준
	 그림자
	 당국
	 신용
	 잔액
# 주제 2
	 비트
	 화폐
	 암호
	 달러
	 거래
	 블록
	 인은
	 금융
	 체인
	 시장
# 주제 3
	 금융
	 부동산
	 비트
	 자산
	 투자
	 신탁
	 디지털
	 시장
	 은행
	 서비스<jupyter_text>1.4. LDA토픽모델링 결과 시각화하기---<jupyter_code># LDA토픽모델링 결과를 시각화하는 pyLDAvis 패키지를 설치합니다.

#!pip install pyLDAvis
# pyLDAvis 패키지를 불러옵니다.
import pyLDAvis.gensim

# LDA토픽모델링 시각화 결과를 출력합니다.
pyLDAvis.enable_notebook()
lda_display = pyLDAvis.gensim.prepare(ldamodel, corpus, dictionary, sort_topics=True)
pyLDAvis.display(lda_display)

# Intertopic Distance Map의 각 주제가 겹치지 않고 잘 구분되면 적절한 토픽개수라고 볼 수 있습니다.
# 그래프의 분포를 확인하며 토픽의 개수를 조정합니다.
# LDA토픽모델링은 분석 수행시마다 매번 결과가 달라집니다.<jupyter_output><empty_output><jupyter_text>2. 감성분석: 문장의 감성수준을 수치로 표현하기---  2.1. 샘플 데이터 불러오기---<jupyter_code># 영화리뷰을 저장할 리스트를 생성합니다.
review_list = []

# 리뷰가 저장된 파일은 data 폴더 안에 저장되어 있습니다.
# 본 자료에서 제공하는 리뷰는 2019년 개봉작 "엑스맨: 다크 피닉스"의 리뷰입니다.
file_path = "data/review_sample.txt"

# 리뷰가 저장된 파일을 불러옵니다.
f = open(file_path, encoding="utf-8")
for line in f:
    review_text = line.strip()
    review_list.append(review_text)
f.close()

print("총 리뷰 수 :", len(review_list))
print("리뷰샘플 :")
print(review_list[10])<jupyter_output>리뷰샘플 :
이게 마지막이라고?? 정말?? 이렇게 끝낸다고??<jupyter_text>2.2. 리뷰 문장을 형태소 단위로 분리하기---<jupyter_code># 꼬꼬마 형태소 분석기를 불러옵니다.
from konlpy.tag import Kkma
kkma = Kkma()

text = "꼬꼬마 형태소 분석기를 불러옵니다."
print(kkma.pos(text))
# 형태소분석 후 기사단위의 형태소 리스트를 저장할 리스트를 생성합니다.
tag_sentence_list = []

# 불용어를 리스트를 정의합니다.
stop_list = ["카페", "카페규정"]

now = 0
for review in review_list:
    now += 1
    print(now, end="\r")
    tag_list = kkma.pos(review)
    tag_sentence = []
    for word, pos in tag_list:
        tag_sentence.append(word + "/" + pos)
    tag_sentence_list.append(tag_sentence)
print("리뷰 형태소분석 샘플 :")
print("- 원문 :", review_list[10])
print("- 결과 :", tag_sentence_list[10])<jupyter_output>리뷰 형태소분석 샘플 :
- 원문 : 이게 마지막이라고?? 정말?? 이렇게 끝낸다고??
- 결과 : ['이것/NP', '이/JKS', '마지막/NNG', '이/VCP', '라고/ECD', '??/SW', '정말/MAG', '??/SW', '이렇/VA', '게/ECD', '끝내/VV', 'ㄴ다고/ECE', '??/SW']<jupyter_text>2.3. 감성사전 불러오기---<jupyter_code># 본 자료에서는 KOSAC(Korean Sentiment Analysis Corpus)이라는 공개 감성사전을 활용합니다.
# 감성사전은 아래 링크에서 다운로드 가능합니다.
# 다운로드한 파일 준 polarity.csv 파일을 활용합니다.<jupyter_output><empty_output><jupyter_text>감성사전 다운로드 URL : http://word.snu.ac.kr/kosac/lexicon.php<jupyter_code># KOSAC 감성사전의 내용을 살펴봅니다.
f = open("polarity.csv", encoding="utf-8")
for line in f.read().split("\n")[:10]:
    line = line.strip().split(",")
    print(line)
f.close()
# KOSAC 감성사전을 불러와 딕셔너리에 저장합니다.
# 원본 사전에 세미콜론(;)으로 구분된 형태소를 띄어쓰기 단위로 변형하여 딕셔너리의 키(key)값으로 활용합니다.

# 사전을 저장할 딕셔너리를 생성합니다.
sentiment = {}

f = open("polarity.csv", encoding="utf-8")
for line in f:
    col = line.strip().split(",")
    pos = col[0]
    polarity = col[7]
    score = col[8]
    # 극성이 POS인 경우 +, NEG인 경우 -를 곱해 스코어(score)를 계산합니다.
    if polarity == "POS":
        score = float(score)
    elif polarity == "NEG":
        score = -float(score)
    else:
        # 긍정 또는 부정인 경우만 취급합니다.
        continue
    # 세미콜론(;)을 띄어쓰기로 바꿔 딕셔너리에 스코어와 함께 저장합니다.
    sentiment[pos.replace(";", " ")] = score

f.close()
# 생성한 감성사전 딕셔너리 샘플을 출력합니다.

# 긍정
print("(긍정) 솜씨/NNG 는/JX 능숙/XR :", sentiment["솜씨/NNG 는/JX 능숙/XR"])
print("(긍정) 자연/NNG 스럽/XSA :", sentiment["자연/NNG 스럽/XSA"])
print("(긍정) 획일/XR 성/XSN 의/JKG :", sentiment["획일/XR 성/XSN 의/JKG"])
print("(긍정) 훨씬/MAG 안전/NNG :", sentiment["훨씬/MAG 안전/NNG"])
# 부정
print("(부정) 뒷맛/NNG 이/JKS 씁쓸/XR :", sentiment["뒷맛/NNG 이/JKS 씁쓸/XR"])
print("(부정) 손해/NNG 배상/NNG 을/JKO :", sentiment["손해/NNG 배상/NNG 을/JKO"])
print("(부정) 에서/JKB 욕/NNG 튀어나오/VV :", sentiment["에서/JKB 욕/NNG 튀어나오/VV"])
print("(부정) 유달리/MAG 난해/XR :", sentiment["유달리/MAG 난해/XR"])<jupyter_output>(긍정) 솜씨/NNG 는/JX 능숙/XR : 1.0
(긍정) 자연/NNG 스럽/XSA : 1.0
(긍정) 획일/XR 성/XSN 의/JKG : 1.0
(긍정) 훨씬/MAG 안전/NNG : 1.0
(부정) 뒷맛/NNG 이/JKS 씁쓸/XR : -1.0
(부정) 손해/NNG 배상/NNG 을/JKO : -1.0
(부정) 에서/JKB 욕/NNG 튀어나오/VV : -1.0
(부정) 유달리/MAG 난해/XR : -1.0<jupyter_text>2.4. 리뷰 감성스코어 계산하기---<jupyter_code># 감성분석 결과를 긍정과 부정 리뷰로 각각 저장할 리스트를 생성합니다.
positive_sentence = []
negative_sentence = []

for tag_sentence, review in zip(tag_sentence_list, review_list):
    # 스코어를 0점으로 초기화합니다.
    score = 0.0
    # 형태소분석된 문장 안에서 최대 7개까지 결합된 형태소가 감성사전 안에 있는지 확인합니다.
    for i in range(len(tag_sentence)):
        max_n = 7
        for n in range(max_n):
            ngram = ""
            for j in range(len(tag_sentence)-n):
                ngram += " " + tag_sentence[j]
            ngram = ngram.strip()
            if ngram in sentiment.keys():
                score += sentiment[ngram]
    # 감성스코어로 긍정 또는 부정을 판단하기위한 임계값을 지정합니다.
    limit = 0
    if score > limit:
        positive_sentence.append(["POSITIVE", str(review)])
    elif score < -limit:
        negative_sentence.append(["NEGATIVE", str(review)])
    
# 감성분석 결과를 감성스코어를 기준으로 정렬합니다.
positive_sentence.sort(reverse=True)
negative_sentence.sort()<jupyter_output><empty_output><jupyter_text>2.5. 리뷰 감성분석 결과 확인하기- 본 자료에서 사용하는 KOSAC 감성사전은 표준어를 바탕으로 제작되었습니다.- 온라인 리뷰의 특성상 문법의 오류가 굉장히 많기 때문에 사전과의 매칭률이 낮을 수 있습니다.- 또한 KOSAC 감성사전의 형태소태깅과 본 자료에서 사용한 꼬꼬마 형태소분석기의 형태소태깅 결과의 차이도 영향을 미칩니다.- 본 자료의 결과가 모두 맞는 것은 아니며 사전기반의 감성분석을 수행하는 방법에 대한 이해를 위해 사용해주세요.- 직접 분석하려는 텍스트의 도메인에 맞는 사전을 추가하여 정확도를 높힐 수 있습니다.---<jupyter_code>print("긍정판별 문장 개수 :", len(positive_sentence))
positive_sentence[:10]
print("부정판별 문장 개수 :", len(negative_sentence))
negative_sentence[:10]<jupyter_output>부정판별 문장 개수 : 13
{'fraction_non_alphanumeric': 0.1306737004066381, 'fraction_numerical': 0.01483679525222552, 'mean_word_length': 3.3046357615894038, 'pattern_counts': {'":': 2, '<': 37, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '371023', 'license': 'Apache-2.0', 'n_tokens_mistral': 6388, 'n_tokens_neox': 7873, 'n_words': 1148, 'path': 'practice-note/12_text-mining-for-beginner_python-text-analysis-2.ipynb', 'repo_name': 'fingeredman/text-mining-for-beginner', 'size': '9109'}