무회blog

001_topics_module-,tomato,ctm,lda,hannanums 본문

Python

001_topics_module-,tomato,ctm,lda,hannanums

최무회 2020. 7. 6. 19:20
001_topics_module-,tomato,ctm,lda,hannanums
In [1]:
#############################################
import tomotopy as tp 
import pandas as pd
import numpy as np
import nltk.stem, nltk.corpus, nltk.tokenize, re ,os ,time
from kiwipiepy import Kiwi
kiwi = Kiwi()
kiwi.prepare()
#############################################
from gensim import corpora 
from gensim import models
from konlpy.utils import pprint
from konlpy.tag import Hannanum
from konlpy.tag import Kkma
kkma = Kkma()
hannanum = Hannanum()
print("success import")

from datetime import datetime
td = datetime.today().strftime("%Y%m%d")
tdd = datetime.today().strftime("%m%d")
now = datetime.now()
tdnow = now.strftime('%Y%m%d%H%M%S')
success import
In [2]:
class Cleaning_Text:
    def listToText(inputList):
        returnText = ''
        for i in inputList:
            returnText = returnText + i + ', '
        rt2 = Cleaning_Text.text_cleaning(returnText)
        return rt2

    ##  # step_4,   공통 코드 , 텍스트 클리닝 
    def text_cleaning(text): 
    ##################################  gensim 사용을 위한 정규표현식 200624
        hangul_path9 = '[가-힣]+\.'                  # 한글로 포함되다 . 

        hangul_path0 = '[가-힣]+\.[가-힣]{1}'        # 한글 . + 한글 처리 
        hangul_path1 = '[가-힣]+\.[\d]{1}'           # 한글 . + 숫자 처리 [0-9]
        hangul_path2 = '[가-힣]+\.[a-z]{1}'          # 한글 . + 영어 소문자 
        hangul_path3 = '[가-힣]+\.[A-Z]{1}'          # 한글 . + 영어 대문자 
        hangul_path4 = '[가-힣]+\.[\S]{1}'           # 한글 . + 비공백 [^ \t\n\r\f\v]와 같다.    
        hangul_path5 = '[가-힣]+\.[\s]{1}'           # 한글 . + 공백 [ \t\n\r\f\v]와 같다.
        hangul_path6 = '[가-힣]+\.[\W]{1}'           # 한글 . + 숫자 + 문자가 아닌 것 [^a-zA-Z0-9]와 같다.
        hangul_path7 = '[가-힣]+\.[\w]{1}'           # 한글 . + 숫자 + 문자 [a-zA-Z0-9]와 같다.
        hangul_path8 = '[가-힣]+\.[\b]{1}'           # 한글 . + 단어 경계 (`\w`와 `\W`의 경계)

        reg_path = hangul_path0 + '|' + hangul_path1 + '|'+ hangul_path2 + '|'+ hangul_path3 + '|'+ hangul_path4+ '|'+ hangul_path5
        hangul = re.compile(reg_path)              # 한글 + 모모로 포함되다 . 

        result = hangul.findall(text)                                                   # 정규식에 일치되는 부분을 리스트 형태로 저장, 단어 반환 
        result = list(set(result))    
        for x in result:
            text = text.replace(x, x[:-1] + '\n' + x[-1:])

        ### 줄단위 좌우 공백 지우기 , 
        text = text.replace('\n','_').split('_')
        text = [x.strip() for x in text]
        tts = ''
        for i in text:
            tts = tts + i + '\n'
            text = tts        
        ##################################  gensim 사용을 위한 정규표현식 200624
        text = re.sub('\[.+?\]','', text)         # 대괄호 [] 이내 모든 문자 삭제     

        #이모티콘 제거
        EMOJI = re.compile('[\U00010000-\U0010ffff]', flags=re.UNICODE)
        text= EMOJI.sub(r'', text)
        #이메일 주소 제거
        email =re.compile('([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)')
        text = email.sub('', text) 
        #URL 제거
        url =re.compile('(http|ftp|https)://(?:[-\w.]|(?:%[\da-fA-F]{2}))+')
        text = url.sub('', text) 
        #HTML 제거
        html =re.compile('<[^>]*>')
        text = html.sub('', text) 

        #특수문자를 공백으로 대체(문장을 살리기위헤 마침표는 남겨둠)
        #special =re.compile('[^\w\s]')
        #text = special.sub(' ', text) 
        special= ['*', '{', ',', ':', ']', '$', '+', '[', '#', '(', '%', '&', '}', '`', '‘', '’','·',
                    '=', ';', '>','>', '/', '"', '“', '”', '\\', '?', '~', "'", '<', ')', '^', '!', '_',
                    '|', '@','@','©','ⓒ', '℗','®','①', '-','▶','…','☞','▲','◆','■'] #'.', 빼고
        for chr in special :
            text=text.replace(chr,' ')

            #특수문자 제거 후 생기는 중복된 공백 제거
            while text.find('  ') > 0:
                text = text.replace('  ',' ' ) # 중복된 공백 제거

            #특수문자 제거 후 생기는 중복된 개행 제거
            while text.find('\n\n') > 0:
                text = text.replace('\n\n','\n' ) # 중복된 개행 제거

            #좌우측 공백 삭제
            text = text.strip()

            # 좌측 공백 삭제
    #         text.lstrip()

            # 우측 공백 삭제
            #text.rstrip() 

        return text 
    def tokenize(sent):
        res, score = kiwi.analyze(sent)[0] # 첫번째 결과를 사용
        return [word
                for word, tag, _, _ in res
                if not tag.startswith('E') 
                and not tag.startswith('J') 
                and not tag.startswith('S')] # 조사, 어미, 특수기호는 제거   
In [3]:
class Tomato_OriginalTopicModel:  # fr_kiwi01_mth
    def tomato_original(text,topics_cnt):  # 텍스트, 토픽개수 
        # tokenize 처리 
        dic01 = {}
        token0 = []
        li_model = [] 
        li_model_PMI = []
        li_model_IDF = []
        li_model_ONE = []

        stemmer = nltk.stem.porter.PorterStemmer() 
        stopwords = set(nltk.corpus.stopwords.words('korean'))         
        
        model     = tp.LDAModel(k=k_cnt, alpha=alpha_cnt,eta = eta_cnt)
        model_PMI = tp.LDAModel(k=k_cnt, alpha=alpha_cnt,eta = eta_cnt, min_cf=min_cf_cnt,tw=tp.TermWeight.PMI)
        model_IDF = tp.LDAModel(k=k_cnt, alpha=alpha_cnt,eta = eta_cnt, min_cf=min_cf_cnt,tw=tp.TermWeight.IDF)
        model_ONE = tp.LDAModel(k=k_cnt, alpha=alpha_cnt,eta = eta_cnt, min_cf=min_cf_cnt,tw=tp.TermWeight.ONE)  # one 모든 단어를 동등하게 보다     
        
        token0 = Cleaning_Text.tokenize(text)
        stopwords = set([wd for wd in token0 if len(wd) <= 1]) #  한글자 단어는 불용어로 지정 
        stopwords = set('기자') #  한글자 단어는 불요어로 지정
        token0 = [wd for wd in token0 if len(wd) > 1]          # 한글자 이상 단어 토큰으로 지정 
        model.add_doc(token0)                                    # tokenize함수를 이용해 전처리한 결과를 add_doc에 넣습니다.
        model_PMI.add_doc(token0)  
        model_IDF.add_doc(token0)  
        model_ONE.add_doc(token0)  
            
        model.train(tran_cnt)    
        for j in range(model.k):
            ttx1= ', '.join(w for w, p in model.get_topic_words(j,top_n=topics_cnt))
            ttx2= ', '.join(w for w, p in model_PMI.get_topic_words(j, top_n=topics_cnt))
            ttx3= ', '.join(w for w, p in model_IDF.get_topic_words(j, top_n=topics_cnt))
            ttx4= ', '.join(w for w, p in model_ONE.get_topic_words(j, top_n=topics_cnt))

            ttx1 = re.sub('[a-zA-Z@.]','',ttx1)
            ttx2 = re.sub('[a-zA-Z@.]','',ttx2)
            ttx3 = re.sub('[a-zA-Z@.]','',ttx3)
            ttx4 = re.sub('[a-zA-Z@.]','',ttx4)

            li_model.append(ttx1)
            li_model_PMI.append(ttx2)
            li_model_IDF.append(ttx3)
            li_model_ONE.append(ttx4)

        dic01['lda_model'] = li_model
        dic01['lda_PMI']   = li_model_PMI
        dic01['lda_IDF']   = li_model_IDF
        dic01['lda_ONE']   = li_model_ONE
        df1 = pd.DataFrame(dic01)
        df1= pd.DataFrame(df1)
        return df1


# df_ = pd.read_excel(filename)
# al_list =  df_['content'].tolist()
# all_list = [al_list[tg_num]]
# text = all_list[0]
# text
# output = Tomato_OriginalTopicModel.tomato_original(text,get_cnt)
# output
In [4]:
# tokenize 처리  # Tomato_OptionTopicModels
class Tomato_HLDAModel:
    def model_exe(topics_cnt,hdl_list,model):
        li_model=[]
        dic02 = {}
        for i, line in enumerate(hdl_list):
            token0 = Cleaning_Text.tokenize(line)
            stopwords = set([wd for wd in token0 if len(wd) <= 1]) #  한글자 단어는 불요어로 지정 
            stopwords = set('기자') #  한글자 단어는 불요어로 지정
            token0 = [wd for wd in token0 if len(wd) > 1]          # 한글자 이상 단어 토큰으로 지정 
            model.add_doc(token0)                                    # tokenize함수를 이용해 전처리한 결과를 add_doc에 넣습니다.
        model.train(tran_cnt) 
        for i in range(model.k):
            ttx1= ', '.join(w for w, p in model.get_topic_words(i,top_n=topics_cnt))
            ttx1 = re.sub('[a-zA-Z@.]','',ttx1)
            li_model.append(ttx1)
            dic02['mdl_HLDAModel'] = li_model
            df3 = pd.DataFrame(dic02)
            return df3
    def tomato_hlda(text,topics_cnt):
        textList = text.split('\n')        
        model = tp.HLDAModel(tw=tp.TermWeight.ONE, min_cf=3, rm_top=1)   # 계층적 LDA ( HLDAModel )
        output = Tomato_HLDAModel.model_exe(topics_cnt,textList,model)
        return output

# df_ = pd.read_excel(filename)
# all_list =  df_['content'].tolist()
# text = Cleaning_Text.text_cleaning(all_list[0])

# output = Tomato_HLDAModel.tomato_hlda(text,get_cnt)
# output
In [5]:
#############################################
class Tomato_OptionTopicModels: # Tomato_OptionTopicModel
    def model_training(topics_cnt,token,model):
        li_model = []
        model.add_doc(token)                                    # tokenize함수를 이용해 전처리한 결과를 add_doc에 넣습니다.
        model.train(tran_cnt) 
        for i in range(model.k):
            ttx1= ', '.join(w for w, p in model.get_topic_words(i,top_n=topics_cnt))
            ttx1 = re.sub('[a-zA-Z@.]','',ttx1)
            li_model.append(ttx1)
        return li_model
    def tomato_options(text):
        dic01 = {}
        token0 = Cleaning_Text.tokenize(text)
        stopwords = set([wd for wd in token0 if len(wd) <= 1])              #  한글자 단어는 불요어로 지정 
        token0 = [wd for wd in token0 if len(wd) > 1]                       # 한글자 이상 단어 토큰으로 지정 
        
        mdl_HDPModel    = tp.HDPModel  (tw=tp.TermWeight.ONE, min_cf=min_cf_cnt, rm_top=1)  # 계층적 디리클레 프로세스 ( HDPModel )
        mdl_CTModel     = tp.CTModel   (tw=tp.TermWeight.ONE, min_cf=min_cf_cnt, rm_top=1)   # 상관 토픽 모델 ( CTModel )
        mdl_PAMODel     = tp.PAModel   (tw=tp.TermWeight.ONE, min_cf=min_cf_cnt, rm_top=1)   # 파칭코 할당 ( PAMODel )
        mdl_HPAModel    = tp.HPAModel  (tw=tp.TermWeight.ONE, min_cf=min_cf_cnt, rm_top=1)   # 계층적 PA ( HPAModel )
        mdl_MGLDAModel  = tp.MGLDAModel(tw=tp.TermWeight.ONE, min_cf=min_cf_cnt, rm_top=1)   # 멀티 그레인 LDA ( MGLDAModel )
        mdl_LLDAModel   = tp.LLDAModel (tw=tp.TermWeight.ONE, min_cf=min_cf_cnt, rm_top=1)   # 라벨 LDA ( LLDAModel )
        
        li_mdl_HDPModel   = Tomato_OptionTopicModels.model_training(get_cnt, token0, mdl_HDPModel  )  # 토픽개수, 토큰단어, 모델
        li_mdl_CTModel    = Tomato_OptionTopicModels.model_training(get_cnt, token0, mdl_CTModel   )
        li_mdl_PAMODel    = Tomato_OptionTopicModels.model_training(get_cnt, token0, mdl_PAMODel   )
        li_mdl_HPAModel   = Tomato_OptionTopicModels.model_training(get_cnt, token0, mdl_HPAModel  )
        li_mdl_MGLDAModel = Tomato_OptionTopicModels.model_training(get_cnt, token0, mdl_MGLDAModel)
        li_mdl_LLDAModel  = Tomato_OptionTopicModels.model_training(get_cnt, token0, mdl_LLDAModel )
        
        dic01['mdl_HDPModel']   = li_mdl_HDPModel[0]
        dic01['mdl_CTModel']    = li_mdl_CTModel
        dic01['mdl_PAMODel']    = li_mdl_PAMODel
        dic01['mdl_HPAModel']   = li_mdl_HPAModel
        dic01['mdl_MGLDAModel'] = li_mdl_MGLDAModel
        dic01['mdl_LLDAModel']  = li_mdl_LLDAModel
        
        df2 = pd.DataFrame(dic01)
        return df2


# output = Tomato_OptionTopicModels.tomato_options(text)
# output
In [6]:
class Hannanum:
    # 텍스트 정제 함수 : 분석에 불필요한 문자는 전부 제거합니다. 
    # 형태소 분석해서 용언(P), 체언(N)만 남김
    def get_infoText(read_text):
        #resList = list()
        resList=[]
        #GetWordSet = set(['N'])
        GetWordSet = set(['N','P'])
        for read_text_line in read_text:
            res=""
            if len(read_text_line) > 0:
                pos = hannanum.pos(read_text_line,ntags=9)
                for keyword, type in pos:
                    # 키워드가 한글자 이상일 경우만
                    if len(keyword) > 1 :
                        # 용언(P), 체언(N)만 남김
                        if (type in GetWordSet):
                            if type == 'P': #용언일 경우 '다'를 붙여줌
                                keyword=keyword+'다'
                            resList.append(keyword)  
        return resList
    
    def hannanum_topics(text, get_cnt):
        texts = []
        w = text
#         for w in all_list:
        raw = w.lower()
        #클린징
        ReadDoc_CleanText= Cleaning_Text.text_cleaning(raw)
        #클린징 마친 텍스트를 문장으로 분리
        ReadDoc_SplitText=ReadDoc_CleanText.split('\n')
        #print("ReadDoc_SplitText=",ReadDoc_SplitText)

        #문장으로 분리한 텍스트를 형태소 분석해서 용언(N), 체언(V)만 남김
        ReadDoc_Analyis=Hannanum.get_infoText(ReadDoc_SplitText)

        ReadDoc_Analyis=pd.Series([x for x in ReadDoc_Analyis if len(x)>1])
        #문서들을 리스트 형태로  texts에 추가
        texts.append(ReadDoc_Analyis)
            
        for i in range(len(texts)) :
            series_keys = texts[i].value_counts().index.tolist()
            series_value = texts[i].value_counts()
        series_keys
        series_keys = series_keys[:get_cnt]
        df0 = pd.DataFrame()
        tts = Cleaning_Text.listToText(series_keys).replace(' ', ', ')
            
        df0['mdl_hannaNum'] = [tts]
        
        return df0
In [7]:
class Get_Result:
    def merge_df(*args):
        
        
        df_original = Tomato_OriginalTopicModel.tomato_original(text,get_cnt)
        df_options  = Tomato_OptionTopicModels.tomato_options(text)
        df_hannnums = Hannanum.hannanum_topics(text, get_cnt)
        
#         df_hlda     = Tomato_HLDAModel.tomato_hlda(text,get_cnt)
        
#         df_rs = pd.concat([df_original,df_options,df_hlda], axis=1)
        df_rs = pd.concat([df_original,df_options,df_hannnums], axis=1)

        remarks = """ 읽기경로: 
        쓰기경로: ./ 
        # get_cnt    = 7       # 토픽의  갯수 , 빈도수에서 자동화 숫자 
        # k_cnt      = 5       # 토픽의 개수    , 행 , 1 ~ 32767 사이의 정수
        # top_n_cnt  = get_cnt # 토픽의  갯수   , 열
        # min_cf_cnt = 1       # 단어 최소 출현 빈도  , 0 일시 모든 단어를 동일하게 봄 
        # alpha_cnt  = 0.1     # 문헌‐토픽 빈도
        # eta_cnt    = 0.01    # 토픽‐단어 빈도
        # tran_cnt   = 200     # 자동학습 빈도                                               
        # rm_top2    = 1     
        ## 1 줄 토픽 , get_cnt 만큼 출력 
        """
        df_rs['remarks'] = remarks
#         df_rs= df_rs[['lda_PMI','mdl_CTModel','remarks']]  ## 필요한 토픽 모델 컬럼 선택하여 선택적 출력 , 
        df_rs= df_rs[['lda_PMI','mdl_CTModel','mdl_hannaNum','remarks']]  ## 필요한 토픽 모델 컬럼 선택하여 선택적 출력 , 
        df_rs.to_excel(td+'_result_Topics.xlsx')
        return df_rs
In [10]:
# 읽기 경로 = './news002/quanbu/'
# 읽을 파일 = filename 
# 쓰기 경로 =  './ '   (path_df 기준)
path_df = './news002/quanbu/'
filename = path_df+'./100_20200701084535_Replace_quanbu_nv.xlsx'
#################################################################################
get_cnt    = 5           # 토픽의  갯수 , 빈도수에서 자동화 숫자 
k_cnt      = 1     # 토픽의 개수    , 행 , 1 ~ 32767 사이의 정수
top_n_cnt  = get_cnt     # 토픽의  갯수   , 열
min_cf_cnt = 1    # 단어 최소 출현 빈도  , 0 일시 모든 단어를 동일하게 봄 
alpha_cnt  = 0.1   # 문헌‐토픽 빈도
eta_cnt    = 0.01  # 토픽‐단어 빈도
tran_cnt   = 200   # 자동학습 빈도
rm_top2    = 1                                   # 최상위 빈도수 삭제 , IDF 방식 
tg_num = 2  # 읽어드린 문서에서 타겟팅 내용 

df_ = pd.read_excel(filename)
al_list =  df_['content'].tolist()
all_list = [al_list[tg_num]]
text = all_list[0]
text
Out[10]:
'하태경 미래통합당 의원은 1일 "추미애 법무부 장관이 윤석열 검찰총장을 때리면서 키워줘 마치 윤석열 선대본부장처럼 (행동하고 있다)"고 비꼬았다.  하 의원은 이날 CBS라디오 \'김현정의 뉴스쇼\' 일일 진행자로 나와 강훈식 더불어민주당 의원과 말을 주고받던 중 최근 추 장관이 윤 검찰총장을 비판한 것에 대해 이같이 평가했다. 하 의원은 "추 장관은 김여정처럼 후계자 되고 싶은 거 아니냐"며 "김여정과 흡사한 그런 톤에 \'잘라먹었다\'며 북한에서 쓰는 말(투를 사용해 윤 총장을 공격했다)"고 지적했다.  이어 "추 장관이 대선 후보로 뜨고 싶은데 잘 안 되고 있어서 지금 계속 반응이 격해지는 거 아닌가"라고 판단했다.  이에 강훈식 의원은 "오히려 야당에서 키우고 싶어 하는 거 아니냐, 이런 문제의식은 있다"며 통합당이 추 장관을 공격할수록 몸값만 높아질 것이라고 했다. 하 의원은 "야당이 누가 키우고 싶어 하겠는가, 경쟁자인데"라고 받아 친 뒤 "윤석열 총장은 박근혜 전 대통령을 감옥에 넣은 사람으로 본인의 일관성이 굉장히 중요한 분 같다"고 윤 총장이 살아있는 권력에도 굴하지 않고 제 길을 가고 있다고 평가했다. 즉 "전 정권에 대해서는 굉장히 엄격하게 했는데 이 정권에 대해서는 솜방망이다? 이건 본인 입장에서는 용납이 안 되는 것 같다"며 "권력이면 저 정권이든 저 정권이든 똑같은 권력이지(라는 생각으로 윤 총장이 움직이는 것 같다)"는 것. '
In [11]:
if __name__ == "__main__":
    start_now = int(time.time())             # 시작 시간 저장
    Get_Result.merge_df()
    
    
    ends = int(time.time()) - start_now
    print('end_now, {} 초'.format(ends))
    start_directory = r'D:\app_src\anaconda\04-srcTest' 
    os.startfile(start_directory)
      lda_PMI mdl_CTModel mdl_hannaNum  \
0  총장, 의원, 장관  의원, 장관, 정권   의원, 장관, 정권   

                                             remarks  
0   읽기경로: \n        쓰기경로: ./ \n        # get_cnt ...  
end_now, 1 초
Comments