#!/usr/bin/env python
# coding: utf-8
# # **NLP (Natural Language Processing) 분석**
# **뉴스그룹 데이터 Set 분석**
# 1. 단어의 지식과 개념을 정리하는 작업으로 **Ontology** 같은 예들이 존재
# 1. 낮은 단계로는 **Tagging, POS(Part of Speech)** 이 있다
# 1. Python 라이브러리로 **NLTK, Gensim, TextBlob** 등이 있다
#
#
# ## **1. NLP 개념 익히기**
#
# **NLTK 에서 자연어 분석 작업**
# 1. **Tokenization**(토큰화) : 텍스트를 공백문자로 구분하여 조각을 나누는 작업 (Document --> Ngram)
# 1. **POS Tagging**(품사태깅) : **Konlpy, NLTK** 에 규격화 된 Tagger를 적용하거나, **POS_TAG**와 같은 함수를 활용
# 1. **NER** (개체명 인식) : **Named entity Recognition** 은 텍스트 문장에서 명사를 식별하는 작업이다
# 1. **Stemming** (어간추출) : 어간, 어근의 **원형으로** 되돌리는 작업
# 1. **Lemmatization**(표제어 원형 복원) : 어간 추출보다 좁은 의미로써 **단어를 유효한 결과로** 출력하는 작업
# In[1]:
# 어간의 추출
from nltk.stem.porter import PorterStemmer
porter_stemmer = PorterStemmer()
porter_stemmer.stem('machines')
# In[2]:
# Token 의 원형을 복구한다
from nltk.stem import WordNetLemmatizer
lemmatization = WordNetLemmatizer()
lemmatization.lemmatize('machines')
# ### **Gensim 에서의 자연어 분석 작업**
# 문장간 유사도 측정을 위해 2008년 시작된 모듈로써, 다양한 모델링이 가능하다
# 1. **Similarity Querying** (유사도 쿼리) : 주어긴 쿼리 객체와 유사한 객체를 검색
# 1. **Word vectorization** (단어 벡터화) : 단어의 동시출현 Feacture를 유지하면서 단어를 표현
# 1. **Distribution Computing** (분산 컴퓨팅) : 다수의 문서를 효과적으로 학습
#
# ### **Text Blob**
# NLTK 기반의 라이브러리로 **맞춤법 확인 및 교정, 언어감지, 번역기능이** 추가
#
# ## **2. newsgroups 데이터 보기**
# 20개의 **News Group을** 대상으로 **11,313 개의** 뉴스 데이터가 담겨있다
# ### **01 News Group 데이터 살펴보기**
# > **fetch_20newsgroups()**
#
# 1. subset : 적제할 데이터를 정의한다
# 1. data_home : 파일을 저장할 폴더 ex) **~/scikit_learn_data**(초기값)
# 1. categories : 추출할 목록을 지정
# In[3]:
# 뉴스 데이터를 가져온다 (약 14MB)
# 뉴스 그룹 0 ~ 19 (20개 목록)
from sklearn.datasets import fetch_20newsgroups
groups = fetch_20newsgroups(data_home='data/news/')
groups['target_names']
# In[4]:
# groups Json 데이터 살펴보기
for key in groups.keys():
print(key, ':', type(groups[key]))
groups.keys()
# In[5]:
# 뉴스그룹 Primary Key 값인 정수값 인코딩
# 정수들이 중복되지 않게 정리된 결과를 출력한다
import numpy as np
print(groups.target)
np.unique(groups.target)
# ### **02 개별 News 데이터 살펴보기**
# 0 번 뉴스의 정보를 확인하기
# In[6]:
# 해당 뉴스의 내용 살펴보기
print("News Group 포함된 자료갯수: {:,} 개\n\n0번 샘플보기: \n{}".format(
len(groups.data), groups.data[0]))
# In[7]:
# 0번 뉴스의 해당 그룹 Category 확인
groups.target_names[groups.target[0]]
# ### **03 데이터 시각화**
# 1. Document를 분석하는 경우 **Bag of Words로써** (단어의 집합) 활용한다
# 1. **단어 모델링과** 어떠한 차이가 있는지를 확인해 본다
# 1. **총 20개 카테고리** 뉴스들을 Histogram으로 시각화 (비슷한 갯수로 모델이 분표)
# In[8]:
get_ipython().run_line_magic('matplotlib', 'inline')
# Seaborn 내부함수에 대한 FutureWarning이 출력
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# 전체 뉴스그룹 데이터의 길이를 시각화 한다
# 전체적으로 11,316개가 비슷한 길이를 갖음을 확인할 수 있다
import matplotlib.pyplot as plt
import seaborn as sns
sns.distplot(groups.target)
plt.grid()
# ### **04 Feacture Set을 활용하여 전체 Text 갯수를 확인한다**
# 1. **Sklearnd의 CountVectorizer를** 활용하여 **빈도상위 500**개 Token으로 Embadding
# 1. **위는 알파벳 갯수로써** 비교를 했기 때문에 비슷하게 나오는데
# 1. **단어 기준으로** Feacture Set을 생성하면 결과가 어떻게 되는지 확인해보자
#
# > **CountVectorizer()** 의 파라미터 확인
#
# 1. **stop_words** : 불용어 목록을 활성화 한다 ex) **None(초기값), english, [a, the, of]**
# 1. **ngram_range** : 추출할 ngram 하한/ 상한선을 지정 ex) **(1,1)(초기값) (1,2) (2,2)**
# 1. **lowercase** : 소문자 변환 활성화 여부 ex) **True(초기값), False**
# 1. **max_feacture** : None 아니면 최대 token 갯수를 지정 ex) **None(초기값), 500**
# 1. **binary** : 바이너리 여부를 정의한다
# In[9]:
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
# 빈도상위 500개의 단어로만 추출한 결과를 분석
cv = CountVectorizer(stop_words="english", max_features=500)
transformed = cv.fit_transform(groups.data)
print(cv.get_feature_names()[:100])
# 빈도상위 100개의 Token을 출력한다 : 문장간 식별력이 낮은 숫자와 기호들이 포함
# In[10]:
# 위에서 추출한 임베딩 데이터로 히스토그램을 보여준다
sns.distplot(np.log(transformed.toarray().sum(axis=0)))
plt.xlabel('Log Count')
plt.ylabel('Frequency')
plt.title('Distribution Plot of 500 Word Counts')
plt.grid(); plt.show()
#
#
# ## **3. newsgroups 데이터 분석**
# 데이터를 전처리, 분석 과정을 단계별 진행한다 (분석에 용이한 텍스트로 재선별)
# ### **01 데이터 전처리 (식별력이 높은 자료들만 추출한다)**
# 1. 식별에 용이한 숫자 기호들은 제외한, 순수한 문자 데이터만 선별한다
# 1. 단어별 일치도를 높이기 위해서 **표제어 복원을** 진행
# 1. 문자중에도 Stopword, name 같이 식별력이 낮은 내용들은 제거한다
#
# ```python
# # 실행 중 nltk 오류시
# import nltk
# nltk.download('names')
# ```
# In[11]:
# 아래에서 사용하는 알파벳 판단함수
'names'.isalpha()
# In[12]:
get_ipython().run_cell_magic('time', '', 'from nltk.corpus import names\nfrom nltk.stem import WordNetLemmatizer\n\n# 영문 제시어만 추출한다\ndef letters_only(astr):\n for c in astr:\n if not c.isalpha(): return False\n return True\n\n# 추출한 데이터 Token을 하나씩 표제어복원을 진행한다\nall_names = set(names.words())\nlemmatizer = WordNetLemmatizer()\ncleaned = [\' \'.join([lemmatizer.lemmatize(word.lower())\n for word in post.split()\n if letters_only(word) and word not in all_names]) \n for post in groups.data]\n \n# cv = CountVectorizer(stop_words="english", max_features=500)\ntransformed = cv.fit_transform(cleaned)\nprint(cv.get_feature_names()[:10])\nlen(names.words())\n')
# ### **02 K-means 를 활용한 클러스터링**
# 1. **(transformed)** : 위 전처리 및 빈도순서 500으로 선별된 데이터 활용
# 1. 데이터세트를 몇 개의 클러스터로 묶는다
# 1. **하드 클러스터링** : 개별 token 이 **1개 클러스터에만** 할당 (엄격)
# 1. **소프트 클러스터링** : 개별 token 이 **다양한 확률값으로 여러 클러스터에** 할당 (유연)
# 1. **이상치** (Outlier) : 어떠한 클러스터에도 할당되지 않는 값을 **이상치, 노이즈라** 한다
#
# > **KMeans(클러스터수, 샘플갯수, 반복횟수)**
#
# 1. **n_cluster** : 클러스터 묶음 갯수 ex) 8 (기본값)
# 1. **max_iter** : 반복자 할당 갯수 ex) 300 (기본값)
# 1. **n_iter** : 다른 초기값으로 알고리즘 재실행 횟수 ex) 10 (기본값)
# 1. **tol** : 실행 중지조건 ex) 1e-4
# In[13]:
get_ipython().run_cell_magic('time', '', "# K Mean를 활용한 묶음처리\nfrom sklearn.cluster import KMeans\nkm = KMeans(n_clusters=20, n_jobs=-1)\nkm.fit(transformed)\n\nlabels = groups.target\nplt.scatter(labels, km.labels_)\nplt.xlabel('Newsgroup'); plt.ylabel('Cluster')\nplt.show()\n")
# ### **03 Topic 모델링**
# 1. 문장 내 단어 Token중 **핵심(주제)가 되는 Token을** 선별한다
# 1. Topic 마다 **다른 가중치를 할당하여 additive model을** 정의한다
# 1. **비음수 행렬 인수분해** : Non-Negative Matrix Factorization
# In[14]:
get_ipython().run_cell_magic('time', '', 'from sklearn.decomposition import NMF\nnmf = NMF(n_components=100, random_state=43).fit(transformed)\n\nfor topic_idx, topic in enumerate(nmf.components_):\n label = \'{}: \'.format(topic_idx)\n print(label, " ".join([cv.get_feature_names()[i]\n for i in topic.argsort()[:-9:-1]]))\n')