Aidemy Tech Blog

機械学習・ディープラーニング関連技術の活用事例や実装方法をまとめる、株式会社アイデミーの技術ブログです。

メンヘラ炸裂!? 西野カナの歌詞から感情の時系列データを抽出してみた

f:id:majisuke:20170805151412j:plain
どーも! まじすけです✨
今回は以下のリンクを参考に、pythonで曲の歌詞から曲中の感情の動きを可視化してみました。
www.statsbeginner.net

「会いたくて 会いたくて」を初め、女性に圧倒的人気を誇る西野カナさん。
よくメンヘラの代名詞とも言われる彼女の曲を可視化してみたら面白いんじゃないかな?ということでやってみました。

f:id:majisuke:20170809141353p:plain

ちなみに「会いたくて 会いたくて」はこんな感じに出ます。(浮き沈みが激しい...)

準備

用意するものはこちら!

  • 西野カナの曲の歌詞
  • python3系
  • MeCab
  • PN Table

日本語の形態素解析MeCabを使うので、使ったことがない人は以下のリンクを参考にしてください。
qiita.com

続いて日本語の感情のネガポジ判定をしてくれる辞書PN Tableをダウンロードしてください。
Macで使う方は辞書が文字化けしてしまうためUTF-8に変換しなければいけません。以下のリンクを参考にファイルをUTF-8形式に変換してください。
kawatama.net

実装

それでは実装に移ります✌︎('ω'✌︎ ) 今回は西野カナさんの「if」を使ってみました!

#まずはもろもろインポート
import re
import csv
import time
import pandas as pd
import matplotlib.pyplot as plt
import MeCab
import random
%matplotlib inline
# 歌詞の読み込み
title = 'nishino'
f = open(title+'.txt')
lyrics_df = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()
# MeCabインスタンス作成
m = MeCab.Tagger('')

このとき歌詞のテキストファイルはプログラムと同じフォルダに入れてください。*歌詞のテキストファイルに関して後で読み取れない部分を省くので、改行などがあっても構いません。
MeCabが上手くいってるかチェックしてみましょう。

print(m.parse(lyrics_df))

f:id:majisuke:20170805144009p:plain

こんな感じに出ればひとまずokです!
続いてネガポジ判定の辞書を読み込みます。

# PN_TABLE辞書の読み込み
pn_df = pd.read_csv('pn_ja.dic',\
                    sep=':',
                    encoding='utf-8',
                    names=('Word','Reading','POS', 'PN')
                   )

次に2つの関数をつくります。
一つはMeCabが解析したあとにでてくる余分な2行を削除して、必要な情報だけを残します。
もう一つはMeCabから作った新しいデータにPN_TABLEの情報を付与します。さらにPN_TABLEに入っていない文字列を削除します。

# テキストを形態素解析して辞書のリストを返す関数
def get_diclist(lyrics):
    parsed = m.parse(lyrics)      # 形態素解析結果(改行を含む文字列として得られる)
    lines = parsed.split('\n')  # 解析結果を1行(1語)ごとに分けてリストにする
    lines = lines[0:-2]         # 後ろ2行は不要なので削除
    diclist = []
    for word in lines:
        l = re.split('\t|,',word)  # 各行はタブとカンマで区切られてるので
        d = {'Surface':l[0], 'POS1':l[1], 'POS2':l[2], 'BaseForm':l[7]}
        diclist.append(d)
    return(diclist)

# PN Tableをデータフレームからdict型に変換しておく
word_list = list(pn_df['Word'])
pn_list = list(pn_df['PN'])  # 中身の型はnumpy.float64
pn_dict = dict(zip(word_list, pn_list))

# 形態素解析結果の単語ごとdictデータにPN値を追加する関数
def add_pnvalue(diclist_old):
    diclist_new = []
    for word in diclist_old:
        base = word['BaseForm']        # 個々の辞書から基本形を取得
        if base in pn_dict:
            pn = float(pn_dict[base]) # pnの中身があれば追加
        else:
            pn = 'notfound' # 中身がなければnotfound
        word['PN'] = pn
        diclist_new.append(word)
    pn_list = []
    textlist = []
    for word in diclist_new:
        pn = word['PN']
        text = word['BaseForm']
        if pn != 'notfound': # notfoundじゃなかったら追加
            pn_list.append(pn)
            textlist.append(text)
    return(pn_list, textlist)

最後にそれぞれの関数を実行し、pandasのDataFrameを使って文字とそのPN値を対応させたデータを作ります。

dl_old = get_diclist(lyrics_df)
pn_list, text_list= add_pnvalue(dl_old)
    
# 本文、PN値を格納したデータフレームを作成
aura_df = pd.DataFrame({'text':text_list,
                        'PN':pn_list,
                       },
                       columns=['text', 'PN']
                      )

結果

ここでデータを可視化してみましょう。

plt.plot(pn_list)
plt.title(title)
plt.show

f:id:majisuke:20170805144858p:plain

こんな感じにプロットできました。かなりマイナスの要素が強い曲でしたね。
歌詞というのは曲の中で時間とともに流れていくので、"感情の時系列データ"を抽出したとも言えるかもしれませんね。