자연어, 비전

카카오톡 대화 내용으로 개인별 워드클라우드(wordcloud) 그리기

H_erb Salt 2022. 6. 22. 11:11

 

내 고향 친구들이 있는 단톡방에는 14명 정도가 있고, 매우 활성화 되어있다.

 

전에도 몇 번 작업해서 친구들한테 공유해준적이 있는데, 카카오톡 대화내용 내보내기 기능을 통해서 받은 txt파일을 워드클라우드로 그려서 공유해주면 친구들이 매우 흥미로워했다.

 

어차피 데분을 하는 입장에서 귀찮을 뿐 어렵지는 않은 내용이다.

 

코드를 직접 AtoZ 짠 건 아니고, 여러 군데서 취합해서 내 입맛에 맞게 조금씩 변형했다.

 

간단한 몇 가지 step으로 워드클라우드를 그려보자.

 

 

 

 

카카오톡 대화내용 내보내기를 하면 .txt 파일로 저장한다. 이를 정규식을 통해서 읽어보자

 

1. 패키지 로드

import re
import pandas as pd
import datetime as dt

from soynlp.normalizer import *
from hanspell import spell_checker
from tqdm import tqdm
from PIL import Image
import numpy as np
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# from khaiii import KhaiiiApi
# from konlpy.tag import Okt

tqdm.pandas()
# okt = Okt()

주석처리한 것들을 제외한 패키지를 설치해주자.

 

hanspell은 네이버 맞춤법검사기를 토대로 맞춤법을 교정해준다.

 

맞춤법 처리를 해준 다음 khaiii나 konlpy에 있는 Okt로 형태소 분석까지 진행하려고 하였으나,

hanspell의 처리속도가 생각보다 더디고, 중간중간 jsondecode error가 뜬다. 

맞춤법을 맞춰주지 않은 상태에서 형태소분석기를 활용해서 전처리를 진행하니까 생각보다 결과물이 만족스럽지 않았다.

어차피 워드클라우드로만 표현할꺼니까 그냥 과감하게 pass하였다.

 

soynlp는 'ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ'를 'ㅋㅋㅋ'로 중복된 글자들을 처리해주는 함수를 제공한다.

 

 

2. 대화내용을 저장한 txt 파일 읽기

def read_kko_msg(filename):
    with open(filename, encoding='utf-8') as f:
        msg_list = f.readlines()

    return msg_list
    
    
def apply_kko_regex(msg_list):
    kko_pattern = re.compile("\[([\S\s]+)\] \[(오전|오후) ([0-9:\s]+)\] ([^\n]+)")
    kko_date_pattern = re.compile("--------------- ([0-9]+년 [0-9]+월 [0-9]+일) ")

    emoji_pattern = re.compile("["u"\U0001F600-\U0001F64F"  # emoticons
                               u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                               u"\U0001F680-\U0001F6FF"  # transport & map symbols
                               u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                               "]+", flags=re.UNICODE)

    kko_parse_result = list()
    cur_date = ""

    for msg in msg_list:
        # 날짜 부분인 경우
        if len(kko_date_pattern.findall(msg)) > 0:
            cur_date = dt.datetime.strptime(kko_date_pattern.findall(msg)[0], "%Y년 %m월 %d일")
            cur_date = cur_date.strftime("%Y-%m-%d")
        else:
            kko_pattern_result = kko_pattern.findall(msg)
            if len(kko_pattern_result) > 0:
                tokens = list(kko_pattern_result[0])
                # 이모지 데이터 삭제
                tokens[-1] = re.sub(emoji_pattern, "", tokens[-1])
                tokens.insert(0, cur_date)
                kko_parse_result.append(tokens)

    kko_parse_result = pd.DataFrame(kko_parse_result, columns=["Date", "Speaker", "timetype", "time", "contents"])
    # kko_parse_result.to_csv("./kakao.csv", index=False)

    return kko_parse_result

 

위 함수를 통해 정규식 코드를 통해서 txt 파일을 dataframe으로 변환한다.

 

 

자 그럼 텍스트 파일이 데이터 프레임으로 잘 변환됐는지 확인해보자.

txt = read_kko_msg('텍스트파일이름.txt')
df = apply_kko_regex(txt)

 

변환된 데이터프레임

아주 적나라하다.

ㅋㅋㅋㅋㅋㅋ

 

 

우리가 wordcloud로 나타낼 데이터는 위 데이터프레임에서 'contents' 컬럼이다. 해당 텍스트를 간단하게 전처리해 주는 작업을 거친 후 워드클라우드로 나타낼 것이다.

 

 

 

3. (매우) 간단한 전처리

clean = re.compile("[^ㄱ-힣0-9 ]")

df['contents_1'] = df['contents'].apply(lambda x: repeat_normalize(x, num_repeats=3))
df['contents_2'] = df['contents_1'].apply(lambda x: clean.sub('', str(x)))
# df['contents_3'] = df['contents_2'].progress_apply(lambda x: ' '.join(okt.morphs(x)))

contents_1번에서는 위에서 언급한 soynlp에서 제공하는 함수를 활용해서 반복되는 단어(예를 들어 'ㅋㅋㅋㅋㅋㅋㅋㅋ')를 줄여준다. num_repeats 파라미터를 조정하여 몇 가지 반복까지 허용할건지 선택한다.

 

contents_2번에서는 조정된 글 중 숫자와 문자, 스페이스를 제외한 나머지 것들을 공백으로 변경한다.

 

주석처리한 콘텐츠 3번에서는 원래 hanspell을 통해 맞춤법 검사를 한 뒤, 이를 형태소 분석한 다음 단어들의 형태소 원형을 추출하는 방법으로 워드클라우드를 그리면 더 정확하게 나오지 않을까해서 해본건데 hanspell자체가 돌아가다 에러가 간헐적으로 계속 뜨더라.

이에 대한 처리를 하지 않고 konlpy나 khaiii같은 형태소분석기를 써서 돌려도 결과는 나오지만, 영 만족스럽지 못했다(아마 친구를 지칭하는 이름이나 기타 용어들을 직접 사전에 등록해주는 작업을 더 추가로 해야 재밌는 결과가 나올 것 같은데, 그렇게까지 하기엔 조금 많이 귀찮았다)

 

 

 

4. 워드클라우드 작성 함수 정의

def color_func(word, font_size, position,orientation,random_state=None, **kwargs):
    return("hsl({:d},{:d}%, {:d}%)".format(np.random.randint(212,313),np.random.randint(26,32),np.random.randint(45,80)))
    
    
def make_wordcloud(name: str, date: str = None):
    sample_df = df[df['Speaker'] == name]
    
    # date 지정 날짜 format: 'YYYY-MM-DD'
    if date is not None:
        sample_df = sample_df[sample_df['Date'] >= date]
        
    txt = sample_df['contents_2']
    txt = (' ').join(txt)
    
    wordcloud = WordCloud(font_path='C:/Windows/Fonts/NanumGothic.ttf', 
                      stopwords=stop_words, 
                      background_color='white', 
                      # colormap='seismic', 
                      color_func=color_func,
                      width=800, height=600, max_words=100)

    gen = wordcloud.generate(txt)
    
    plt.figure(figsize=(12,12))
    plt.imshow(gen)
    plt.axis('off')
    plt.show()

첫 번째 color_func 함수는 워드클라우드로 그려주는 색상을 예쁘게 표현해주기 위한 함수이다.

 

두 번째 make_wordcloud 함수를 써서 직접 워드 클라우드를 그린다. 이 때, 날짜를 기준으로 끊어서 표현하고 싶은 경우 date 파라미터를 지정해주면 되고, 'YYYY-MM-DD' 형식으로 string으로 입력해주면 그 날짜부터 내보내기를 한 시점까지의 데이터를 써서 워드클라우드를 그려준다.

 

그리고 make_wordcloud 함수 안에서 정의한 wordcloud 중 font_path를 정의한 부분이 있다. 이건 한국어를 나타내기 위해 나눔고딕 패스를 정의한건데, 폰트 경로를 여기서 설정해주면 되겠다.

 

 

 

5. 사용자 정의 불용어 작성

stop_words = ['ㅋ', 'ㅋㅋ', 'ㅋㅋㅋ', '샵검색', '사진', 'kakao com', '아', '좀', '나는', '나도', '또',
             '내', '아니', '더', 'vkakako', '저거', '한', '시세1일', '많이', '내가',
             '왜', '다', 'comv', '진짜', 'ㅇㅇ', '못', '나', '이거', '그', 'v', 'kakao', '시세 1일', 'http', 'https',
             '지금', '그거', '뭐', '시세', '1일', '이제', '함', '같은데', '우리', '아직', '하고', '바로', '그럼', '누가',
             '저', '듯', '거', '아니냐', '하면', 'com', '와', 'ㅅㅂ', 'ㄷㄷ', 'ㅎㅎ', 'www', 'youtube', '난',
             'watch', 'ㄹㅇ', '이모티콘', '잘', '그냥', 'ㅜㅜ', 'ㅠㅠ', '같이', '그때', 'ㅜ', '오', '그래서', 'ㅌㅌ', 'ㅡㅡ', '아니라', '걍',
             '허허', 'ㅠㅠㅠ', 'ㅜㅜㅜ', '이게', '흠', '캬', '이', '그게', '있음', '같은', '거의', '다시', '일단', '계속', '하', '제일',
             '딱', '후', '사람은', '응', '그런', '그렇게', '있는데', '사람이', '그렇게', '2장', 'ㅇㅇㅇ', '무슨', '보면', '아니면', '그건', '다른',
             '그리고', 'ㅠ', '먹고', '한번', 'ㄲㅂ', '안', 'short', '않나', '때', 'youtu', 'be', '동영상', 'feature', 'share', 'shorts', '아님', '이번에', '하네',
             'ㅇㅅㅇ', 'ㅈㄴ', '니', '수', '그래도', '잇나', '이미', '삭제된', '메시지입니다', 'ㅇ', 'me', 'naver', 'ㅎ', 'ㄱㄱ', 'ㄴㄴ', 'ㄷ', 'ㄱ', 'ㅏ',
             '음', '머', 'ㅌㅋ', 'ㅌㅋㅋ', 'ㅌㅋㅋㅋ', 'ㄷㄷㄷ', '막', '니가', 'ㅎㅎㅎ', 'ㅋㅎ', 'ㅎㄷㄷ', '근데', '오늘', '존나', '어제', '요새', '있나', '가', '됨',
             '할', '왔는데', '같음', '곳에', '중', '같다', '는', '도', '하지', '하는', 'ㅡ', '해서', '없어서', '게', '될', '말고', '전에', '가면', '있어', '하는데',
             '된다', '해도', '하던데', '싶다', '갈', '없다', '되지', '있다', '했는데', '꽤', '한다', '했다', '돼', '해라', '중에', '나옴', 'ㅘ', '있는']

 

리스트로 추가 처리해줄 불용어를 작성해주자. 느낌 따라서 없애고 싶은 단어들을 설정해주면 된다.

 

 

 

6. 워드클라우드 그리기

완성된 워드클라우드. 친구의 성향과 나는 iid 임을 밝힌다 ^^

 

톡방에 공유해서 맘껏 즐겨주자.

워드클라우드 결과만 공유해서 누가 올린건지 맞추기 했는데 생각보다 엄청 쉽더라.ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

 

 

 

 

 

 

 

'자연어, 비전' 카테고리의 다른 글

transformer 구현 및 설명  (0) 2021.06.03
시퀀스 모델링  (0) 2020.11.10
워드 임베딩  (0) 2020.11.10
단어 유사도 정리  (0) 2020.11.09
자연어 처리를 위한 전처리 과정 정리  (0) 2020.11.06