심심해서 하는 블로그 :: 심심해서 하는 블로그


영화 내용은 안 궁금하고 재밌는지 궁금한 사람들을 위한 선 요약

1. 마지막 20분을 위한 영화

2. 라라랜드가 재밌었다면, 이 영화도 재밌을 것 입니다.

3. 어? 나도 모르게 Queen 노래를 Playlist에 담고 있네? 



Queen 음악을 좋아하고, 즐겨듣던 사람으로써 퀸 음악을 영화관에서 듣게 된다는 것 만으로 행복했던 영화였다.

이 영화의 파급력이 대단하다고 느끼는 것은 동네 편의점에서 Queen 노래 그것도 Radio gaga를 듣을 수 있다는 것이다.


영화 보헤미안 랩소디는 퀸의 탄생부터 퀸의 전설적인 공연으로 기록되는 Live AID 까지 10년 가까히의 시간들을 영화에 담고자 했다.

영화의 앞부분은 퀸의 성공적인 모습을 압축하여 보여 주고자 하다 보니, 서사가 단순하다.


두 사람이 감정적으로 싸우다가 다른 멤버가 이거 해보자 하면, 오 좋은데? 하고 획기적인 노래를 만드는 것이 두여차례 반복한다.


그런데 이런 서사가 노래가 좋으니까 먹힌다.


We will Rock You와 보헤미안 랩소디, Another one bites the dust 가 보통 좋은 노래가 아니니까..


그 사이에 프레디 머큐리의 정체성을 찾아가는 과정을 담고 있다.

자신이 동성을 좋아한다는 것을 인정하면서 고독과 고뇌를 담고자 했다. 

그 시기부터 프레디가 퀸과 멀어지고 음악을 뒤로 하고 술과 약으로 사는 방탕하는 삶을 살게되면서 그를 죽게 만든 에이즈에 걸린다.


(물론 실제로 프레디 본인이 에이즈인 것을 알게 된 시기는 Live AID 이후의 일이지만, 영화의 전개를 위해 필요한 픽션이라고 생각한다.)


동성애 구간을 아주 어둡게 표현하고, 이성애 구간을 밝고 낭만적으로 표현한거에 불만을 가질 수도 있지만, 당대 시대상을 반영한 것이라 생각한다.


다시 가족과 Queen으로 돌아와 Live AID 공연으로 하는 것으로 영화는 마무리된다.



Live AID 공연을 보여준 20분은 환상적이였다.

유튜브로 보는 공연에서 카메라 앵글이 다소 아쉬울 때가 많았는데, 다양한 앵글로 공연을 볼 수 있어서 좋았다.

그리고 저 공연을 직접 경험하지 못한 세대로써 생생한 관객들의 떼창은 마치 현장에서 그의 공연을 보는 듯한 경험을 제공해주었다.


이렇게 영화는 끝이 난다.


퀸의 다큐멘터리를 봤을 때 마음 아프고 프레디 머큐리가 대단하다고 느낀 부분은 Live AID의 모습이 아닌 에이즈에 걸려 힘들었음에도 불구하고, 죽는 마지막 순간까지 음악을 사랑했다는 것이였다.


퀸을 처음 접하는 사람들에게 알려주고 싶은 아쉬움은 있있다. 프레디가 이렇게 열정적이였다고!!! 

하지만 마음의 아픔이 되었던 날이였고, 좋은 기억들만 가지고 퀸을 좋아해주길 감독이 바라는 것 아닐까 생각한다.


2시간 20분이 전혀 길지 않게 느껴지는 영화였다.

언젠가 왓챠플레이나 넷플릭스에 보헤미안 랩소디가 올라오는 날이 온다면 마지막 20분은 꼭 다시 경험해보고 싶다.

'눈누난냐 > 영화' 카테고리의 다른 글

[영화] 도어락 리뷰  (3) 2018.12.11
암수살인을 보고 왔습니다 (스포주의)  (2) 2018.10.26
,

rGithub > https://github.com/ssooni/data_mining_practice

- 자세한 소스코드는 Github에 올렸습니다.

- 데이터 파일도 올려 드리고 싶지만 실제 카카오톡 대화내용이라 일부분만 올렸습니다.

- 소스코드는 짬짬히 업로드할 예정입니다.




카카오톡 시리즈의 2번째로 워드클라우드를 만들어 보았습니다.

이전 시간에서 만들었던 명사 추출 결과를 사용하여 워드클라우드를 그릴 것입니다.

 

1. 단어의 빈도 수를 측정  

워드클라우드에서 글자 크기를 정하는 기준을 저는 단어의 빈도수로 결정하였습니다. 

특정 컬럼을 그룹으로 하여 집계하는 방법은 pandas에서 간단하게 구현이 가능합니다.


mwordcloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def draw_wordcloud(kkma_result):
    # List로 되어있는 열을 Row 단위로 분리
    tokens = pd.DataFrame(kkma_result["token"].apply(lambda x: ast.literal_eval(x)).tolist())
 
    tokens["Date"= kkma_result["Date"]
    tokens["Speaker"= kkma_result["Speaker"]
    tokens["timetype"= kkma_result["timetype"]
    tokens["time"= kkma_result["time"]
    tokens["contents"= kkma_result["contents"]
    
    tokens = tokens.set_index(["Date""Speaker""timetype""time""contents"])
    tokens = tokens.T.unstack().dropna().reset_index()
 
    tokens.columns = ["Date""Person""time_type""time""sntc""index""token"]
    print(tokens.head())
 
    # 빈도수 집계
    summary = tokens.groupby(["token"])["index"].count().reset_index()
    summary = summary.sort_values(["index"], ascending=[False]).reset_index(drop=True)
 
    """ 이하 생략 """ 
cs


모든 소스코드는 GitHub를 참조바랍니다.


제가 정의한 draw_wordcloud()는 집계를 하고 빈도수가 많은 순서대로 정렬한 후 워드클라우드를 생성합니다.


line 2-15

시리즈 1의 결과를 보면 token 컬럼 안에 리스트로 토큰들을 저장하도록 구성되어 있습니다. 


이렇게 구성되어 있으면 분석하기 상당히 불편하므로 리스트 안에 있는 원소를 풀어서 새로운 행을 구성하는 것이 line2 - 14 입니다.

만약 시리즈를 이어서 하는 것이 아니라면 line 2-14번은 생략하셔도 됩니다.


line 18 - 19

pandas.DataFrame의 groupby는 특정한 컬럼을 그룹으로 묶어줍니다.

그리고 그룹을 대상으로 min(), max(), count()등의 집계함수를 제공합니다. 

이 경우에는 모든 구간에 대하여 token을 기준으로 발생한 빈도수를 집계하였습니다. 

순위를 측정하기 위해서 집계한 결과를 내림차순으로 정렬까지 완료합니다.


2. 워드클라우드  

데이터과학에서 시각화는 생각보다 큰 비중을 차지하는 분야입니다. 

어떻게 보여줘야 분석에 용이하고 데이터 분석결과를 받아 드리는 사람들이 쉽게 받아 드릴수 있기 때문입니다.

측정한 빈도수를 기반으로 Line나 막대 그래프도 그릴 수 있습니다만, 몇 번 나왔는지에 큰 의미를 두지 않는다면 빈도수는 TMI가 될 수 있습니다.

키워드에만 집중하기 위한 시각화 표현 기법으로 주로 워드클라우드를 사용합니다.

그리고 Python의 WordCloud 모듈은 개발자들에게 쉽고 빠른 방법으로 워드클라우드를 생성하는 것을 도와줍니다.


>> pip install wordcloud


워드클라우드 모듈을 설치가 완료되었다면 이제 사용하는 소스 코드를 작성해 봅시다.




mwordcloud.py

1
2
3
4
5
6
7
8
def draw_wordcloud(kkma_result):
 
    """ 상단 생략 """
 
    wc = WordCloud(font_path='./font/NanumBrush.ttf', background_color='white', width=800, height=600).generate(" ".join(summary["token"]))
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
cs


line 4

WordCloud의 generate() 함수는 공백으로 분리된 문자열 리스트를 받습니다.

그리고 가장 첫 번째의 단어를 가장 크게, 가장 마지막 단어를 가장 작게 표현합니다.

앞서 빈도 수를 기준으로 token을 정렬하였기 때문에 가장 많은 빈도 수가 나오는 문자가 가장 크게 나올 것입니다. 

아래 그림처럼 말이죠.


 

카카오톡은 이모티콘과 사진, 동영상을 전송한 채팅 기록에 대하여는 (이모티콘) 이런 식으로 저장하니까 이모티콘과 사진이 압도적으로 많이 나오는 걸 볼 수 있습니다.


3. 특정 단어를 제외하자  

정말 직업으로 이모티콘을 개발하는 사람끼리 얘기하는 것이라면 이모티콘 단어가 큰 의미로 받아 지겠지만, 그냥 개발자와 그 여자친구간에 얘기에서 이모티콘은 큰 의미가 있는 단어는 아닙니다.

이번 단계에서는 노이즈라고 생각하는 단어들을 제외하는 과정을 진행해서 유효한 단어들만 보여주고 싶습니다.


mwordcloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def get_except_keyword(filename):
    keyword_list = list()
    with open(filename, encoding='utf-8') as f:
        for keyword in f.readlines():
            keyword_list.append(keyword.strip())
    print(keyword_list)
    return keyword_list
 
def draw_wordcloud(kkma_result):
 
    """ 상단 생략 """
    
    # 특정 단어 필터링
    except_keyword = get_except_keyword("./raw_data/except_word.txt")
    summary = summary[summary["token"].apply(lambda x: x not in except_keyword)]
    summary = summary[summary["token"].apply(lambda x: len(x) > 1)]
 
    wc = WordCloud(font_path='./font/NanumBrush.ttf', background_color='white', width=800, height=600).generate(" ".join(summary["token"]))
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
 
cs



./raw_data/except_word.txt 에는 아래와 같이 제외하고자 하는 단어들의 리스트가 저장 되어있습니다.


./raw_data/except_word.txt


line 14 - 16

get_except_word() 함수에서 해당 파일을 읽어서 리스트 형태로 제외 문자열을 가지고 있습니다.

line 15에서 해당 문자열 리스트 안에 token이 포함되어 있으면 제외하는 로직이 적용됩니다.

그리고 추가적으로 line 16에서 token의 글자 수가 1개인 경우도 제외하였습니다.



아까보다는 저한테는 필요한 것이 나오긴 했지만, 좀 더 필터링을 한다면 더 좋을 거 같아요.

근데 사각형 너무 식상하지 않나요?


4. 워드클라우드 Mask 적용

Mask를 적용하면 워드클라우드가 이미지에 따라서 맞춰서 단어를 배열해 줍니다. 

바탕색이 투명한 PNG 파일을 하나 준비합니다.

저는 여자친구가 좋아하는 데덴네 이미지를 하나 준비하였습니다.


mwordcloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from PIL import Image
 
def draw_wordcloud(kkma_result):
 
    """ 상단 생략 """
    
    denne_mask = np.array(Image.open("./font/denne.png"))
 
    wc = WordCloud(font_path='./font/NanumBrush.ttf', background_color='white', width=800, height=600, mask=denne_mask).generate(" ".join(summary["token"]))
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
 
cs

 

line 7

마스크로 사용할 이미지를 읽어서 Numpy.Array로 저장합니다. 


line 9

WordCloud 생성자에 mask 파라미터에 line 7에 만들어 둔 마스크를 넣어 줍니다.

 


실행한 결과입니다. 

원하는 이미지를 사용해서 적용해 보는 것이 좋을 거 같아요.

다음 시리즈는 Word2Vec을 적용해본것을 올려 볼까 합니다.


전체적인 소스코드는 GitHub를 참조하시고, 궁금한 사항이 있으시면 댓글 달아 주시면 최대한 빨리 답변드리겠습니다.


긴 글 읽어주셔서 감사합니다.

공감버튼은 작성자에게 큰 동기부여가 됩니다.

,

- 자세한 소스코드는 Github에 올렸습니다.

- 데이터 파일도 올려 드리고 싶지만 실제 카카오톡 대화내용이라 일부분만 올렸습니다.

- 소스코드는 짬짬히 업로드할 예정입니다.


오래 전부터 시도를 해보고 싶었지만, 카카오톡 대화 데이터를 이용해서 워드 클라우드부터 Word2Vector 등등 텍스트 마이닝에서 사용하는 기법을 사용해 볼려고 합니다.

통계적 지식은 거의 전무하지만, 1년간 우연히 텍스트 마이닝 프로젝트를 하면서 어깨 너머 보고 배운 것을 기반으로 소소한 프로젝트를 만들어 보았습니다.


프로젝트 폴더 구조


1. 카카오톡 대화를 Text 파일로 저장합니다.

컴퓨터 카카오톡을 켜서 아무 대화 방에서 Ctrl + S를 누르면 대화를 텍스트 파일로 저장이 가능합니다.

저는 여자친구와 나눈 대화를 실험 재료로 사용하고자 합니다.



그리고 프로젝트의 raw_data 폴더에 넣어 둡니다.

저는 raw_data/kko.txt 파일로 저장해두었습니다.



2. 정규표현식

정규표현식은 찾고자하는 텍스트가 일정한 패턴을 가지고 있을 때, 해당 패턴을 표현하기 위해 사용하는 언어입니다.

가독성이 썩 좋지 않아서 처음에 진입하기 꺼려지지만, 알아두면 텍스트에서 원하는 부분만 가져오기 편합니다.

카카오톡에 저장된 내용을 보면 다음과 같은 패턴을 알 수 있습니다.



언젠가 사용하리라 생각하면서, 일자 정보누가 몇 시어떤 말을하는지까지 패턴을 이용하여 추출하고자 합니다.

위의 패턴을 정규표현식으로 표현하면 다음과 같습니다.



꼼꼼하게 날짜의 패턴에서 숫자의 갯수를 지정하거나, 시간 부분도 표현이 가능하지만, 정형화된 패턴이라 너프하게 표현했습니다.  

이제 Python으로 정규표현식을 적용해봅시다.


regex.py의 일부분

1
2
3
4
5
6
7
8
9
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)
cs


파이썬의 re package으로 정규표현식을 컴파일하여 문자열에서 패턴 추출을 할 수 있도록 제공합니다.

카카오톡 대화 부분과 날짜 패턴을 생성합니다. 그리고 추가적으로 이모지 패턴을 넣어줍니다. 

(이모지 패턴은 StackOverflow를 참고하여 작성하였습니다.)

이모지 패턴은 이후에 사용할 형태소 분석기 꼬꼬마(Kkma)에서 이모지를 만나면 에러가 발생해서, 이모지를 제거하기 위한 목적으로 만들었습니다.

regex.py의 일부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def apply_kko_regex(msg_list):    
 
    """ 상단 생략 """
 
    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("./result/kko_regex.csv", index=False)
 
    return kko_parse_result
cs

이후의 코드는 파일에서 읽은 메세지를 정규식을 이용하여 패턴을 추출하고 이모지의 경우 삭제하는 코드입니다.


line 14

문자열 Type str의 내장 함수 findall()은 정규식 패턴에 해당하는 모든 문자열을 추출해줍니다. 


line 18

특정 패턴을 가진 문자열을 치환하는 경우 re.sub()를 사용합니다.

re.sub(패턴, 치환 문자열, 대상 str) 

지금의 경우에는 모든 이모지 패턴의 문자를 메세지 텍스트에서 삭제하기 위해 사용하였습니다.


line 22-23

추출된 결과를 pandas.DataFrame에 저장하고 csv 파일로 저장합니다.





3. 한글 형태소 분석기 적용

우리가 글을 배울 때, 단어를 익히고 문장을 배우고 문장들을 이어서 문맥을 완성합니다. 

이처럼 텍스트를 분석할 때 처음부터 문장 전체를 사용하지 않습니다. 

작은 단위로 쪼개는 과정을 Tokenizing 라고 하는데, 형태소 단위까지 쪼개는 경우도 있고, 단어 중심으로 쪼개는 경우도 있습니다.


저는 한글 형태소 분석 모듈 KoNLPy를 이용하여 형태소 단위로 쪼개는 것을 선택하였습니다.

설치는 Anaconda Prompt에서 아래의 명령어를 입력하면 설치가 가능합니다.


>> pip install konlpy


KoNLPy에는 Hannanum, Kkma, Komoran 등의 형태소 분석 패키지를 제공하는데 저는 Kkma(꼬꼬마)를 사용했습니다.

특별한 이유는 없습니다.. 꼬꼬마라는 네이밍이 귀여워서 선택했을 뿐..


kkma_token.py의 일부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re
import pandas as pd
from konlpy.tag import Kkma
 
 
def get_noun(msg_txt):
    kkma = Kkma()
    nouns = list()
    # ㅋㅋ, ㅠㅠ, ㅎㅎ  
    pattern = re.compile("[ㄱ-ㅎㅏ-ㅣ]+")
    msg_txt = re.sub(pattern, "", msg_txt).strip()
 
    if len(msg_txt) > 0:
        pos = kkma.pos(msg_txt)
        for keyword, type in pos:
            # 고유명사 또는 보통명사
            if type == "NNG" or type == "NNP":
                nouns.append(keyword)
        print(msg_txt, "->", nouns)
 
    return nouns
cs


line 10 - 11
카카오톡 데이터로 분석하다보니 ㅋㅋ ㅎㅎ 등을 제거하는 로직을 추가하였습니다.
ㅋㅋ, ㅎㅎ, ㅜㅜ는 그래도 꼬꼬마에서 무난하게 이모티콘으로 해석해 줍니다.
하지만 "ㅁㄹ데ㅂ제더레ㅐ벚대ㅏ에"와 고양이가 타이핑한 게 들어가니까 성능이 매우 느려져서 제거하는 것을 추가 했습니다. 

line 14
물론 kkma.nouns() 라는 명사만 따로 추출하는 함수를 제공해주는데, 모든 조합 가능한 명사를 제공하는 건지 마음에 안들어서 kkma.pos()를 사용하여 문장에 속하는 것들의 품사를 분석한 뒤 그 중에 명사나 고유 명사에 해당하는 것만 추출했습니다.

결과물


카카오톡 특징상 맞춤법, 띄어쓰기가 전혀 안되있다보니 정확성은 떨어집니다.

서로간의 애칭이나 신조어가 들어가는 경우에 더욱 이상하게 분리를 하는데 이 경우에는 사전에 추가를 해주면 됩니다.



4. Kkma 사전에 키워드 추가

Anaconda 설치경로/Lib/site-packages/konlpy/java 경로로 이동합니다.

kkma-2.0.jar의 압축을 풀어 준 후 폴더로 이동합니다.

폴더 안에 dic 폴더에 사전들이 있습니다.


해당 사전에 마지막에 추가하고자 하는 내용을 넣어 줍니다.

명사만 저는 추가하였습니다.




이전 폴더로 이동해서 jar 파일로 다시 아카이브한 후 덮어쓰면 사전이 적용 됩니다.


카카오톡 대화 내용에 형태소 분석기를 적용하여 보았습니다.

다음 포스팅에서는 워드클라우드를 생성하는 것을 주제로 찾아 뵙겠습니다.


도움되셨다면 공감버튼 눌러주세요.

공감 버튼은 작성자에게 큰 동기부여가 됩니다.



,