前処理を行いツイートをWordCloudで可視化する方法

2020-03-31
2020-03-31

本記事では、Twitter API で集めたツイートに前処理を行い、WordCloud で可視化する方法を紹介します。

最終的にこんな感じになります。

<script>

Twitter API やツイート収集システムについては以下の記事で紹介しました。

前処理概要

日常用いられる文章は実は様々な形をしていて、そのままではプログラムに読み込ませて解析をするのは難しいです。

そこで、自然言語処理では解析に入る前にテキストを解析に適した状態にする前処理が必要になります。

今回行う具体的な前処理の中身は以下の通りです。

  • 不要情報の除去(記号・URLなど)
  • 正規化(大文字小文字変換、半角全角統一、数字を0になど)
  • 形態素解析(単語分割、品詞指定)
  • ストップワードの除去

おそらくもっと正確にやるなら同義語辞書による変換も必要になるでしょう。

ユーザー辞書登録については以前紹介した記事で行っているので、形態素解析で反映されてます。

ツイートデータを前処理

Twitter API で収集したツイートに前処理を行います。

実行環境は以下の記事にあるように Docker で行い、コードは Jupyter NoteBook に書いていきます。

まずは pandas でツイートを保存した csv を読み込みます。

import os
import pandas as pd
import numpy as np
import emoji
import spacy
import re
import neologdn
import json
from wordcloud import WordCloud
from collections import Counter
path = "<file path>"
file_name = "<file name>.csv"
df = pd.read_csv(os.path.join(path,file_name))
# 必要な列を定義。textがツイート、user_mentionsはツイートに含まれるメンション
column = ["text", "user_mentions"]

次にツイートに含まれるメンションを除きます。メンションは API の結果にツイート本文とは別に抽出できるようになっているので、csv 作成時にあらかじめ列にいれておきました。

bool_list = df["user_mentions"].isna()
for idx, bol in enumerate(bool_list):
    if bol:
        continue
    else:
        mention_list = df["user_mentions"].copy().loc[idx].split(",")
        for mention in mention_list:
            df["text"].loc[idx] = df["text"].copy().loc[idx].replace("@"+mention, '')

python 的にあまりよくない書き方かもしれないですが、とりあえずこれでメンション部分をツイートから除外できます。

さて、いよいよ前処理部分です。(メンション除外も前処理っぽいですが、、)

def preprocess_wordcloud(doc):
    # 絵文字削除
    doc = ''.join(['' if c in emoji.UNICODE_EMOJI else c for c in doc])
    print("絵文字削除")
    # 不要記号削除
    pattern = '[!"#$%&\'\\\\()*+,-./:;<=>?@[\\]^_`{|}~「」〔〕“”◇ᴗ●↓→♪★⊂⊃※△□◎〈〉『』【】&*・()$#@。、?!`+¥%]'
    doc =  re.sub(pattern, ' ', doc)
    print("不要記号削除")
    doc = neologdn.normalize(doc)
    print("正規化")
    # 大文字・小文字変換
    doc = doc.lower()
    print("大文字小文字変換")
    # 数字を0に
    doc = re.sub(r'\d+', '0', doc)
    print("数字をゼロに")
    # 形態素解析
    print("形態素解析開始")
    nlp = spacy.load('ja_ginza')
    doc = nlp(doc)
    x = []
    for sent in doc.sents:
        for token in sent:
            if token.pos_ in ["PROPN","NOUN"]:
                x.append(token.lemma_)
    print("形態素解析完了")
    with open("<stopword file path>.txt", "r", encoding="utf-8") as f:
        stopwords = [word.replace('\n', '') for word in f.readlines()]
    x = [c for c in x if c not in stopwords]
    print("ストップワード除去")
    print(len(x))
    return x
text = preprocess_wordcloud(doc)

形態素解析の結果のうち、固有名詞・名詞だけを抽出するようにしてます。指定する POS タグについてはこちらの記事が見やすいです。

実行結果は以下のようになります。

前処理結果

自分のコードの書き方だと形態素解析が思ったより時間がかかります。といっても数分程度です。ただメモリ 8GB の古い PC だと処理が終わらなかったです。

WordCloud の作成

続いては前処理済みのデータで WordCloud を作成します。

# リストをスペース区切りの分かち書きに
text_list = " ".join(text)
# 検索ワードは多すぎるので除外
text_list = text_list.replace("ブログ","")
text_list = text_list.replace("初心者","")
# WordCloud定義
wordcloud = WordCloud(width=600, height=400,font_path="/root/.fonts/NotoSansCJKjp-Regular.otf",background_color="black",collocations = False)
# テキストからワードクラウドを生成する。
wc = wordcloud.generate(text_list)
#画像ファイルとして保存
wc.to_file('wordcloud-ブログ-初心者.png')

これで WordCloud が生成できます。collocations=Falseとすることで複合語を表示しないようにできます。

結果は以下です。ちなみにデータは Twitter API で「ブログ 初心者」と検索して得られた結果です。

wordcloud ブログ初心者

本記事の最初でツイートにのせたものはツイートの動詞や形容詞なども含まれており、さらに複合語もありにしていたのでやや見づらいです。

名詞・固有名詞と複合語をなしにするとシンプルで見やすいです。

WOrdCloud の結果については、「記事」はわかるとしても「収益」がこんなデカさになるのは面白いですね。みんな収益目当てでやっていると推測できます。

ハマったところ

ハマったところは以下です。

  • GiNZAで使われているSudachiPYのユーザー辞書登録
  • WordCloudの文字化け回避

1 つ目については、Docker で環境を作成したのですが、どこのパスに必要な sudach.json などがあるのか混乱しました。また、GiNZA で使われているものとインストールされた SudachiPy の方にも同じようなファイルがあるので、どちらが正しいのかもわからなかったです。

結局、公式のドキュメントをしっかり読んだら解決できました。

2 つめの WordCloud の文字化けは、フォントパスを指定しないと日本語が文字化けします。 これは情報がネットにたくさんあったのでそこまではまらなかったです。どちらかというと、日本語フォントをダウンロードして設定する工程を自動化するのに時間がかかりました。

ここらへんのハマりポイントも前回の記事の環境構築で回避できているはずです。

まとめ

本記事では GiNZA を使ったツイートの前処理について書きました。

かなり基本的な前処理しかしていませんが、単語を眺めるには十分でした。

GiNZA はもっと高度なことが可能で他の機能も使ってさらに有意義な解析も行ってみたいです。

以下の記事が GiNZA の使い方についてかなり詳しく書いてあります。

参考書籍