2018年1月31日水曜日

KH Coderでツイートを分類する

KH Coderというフリーソフトウェアを使うとテキストデータを統計的に分析できる。GUIで操作できるので、RやPythonよりも手軽に分析を行える。ブログをスクレイピングしてKH Coderで分析するでは、ブログテキストからの頻出語の抽出や、共起ネットワークの作成をやってみた。KH Coderには他にもいろいろな分析機能があって、そのひとつが文章のクラスター分析。KH Coderが出力した分析結果を解釈するでは「語」単位でクラスターに分けたが、文書のクラスター分析では文章単位でクラスターに分けることができる。今回は、この文章クラスター分析でツイートを分類してみる。


環境


Raspbian Jessie(ツイートの収集環境)


Windows 10(KH Coder環境)
Windows版 KH Coder Version 3.Alpha.11a


ツイートの収集


まずは分析するツイートを収集する。対象は「スイーツ シュークリーム」「スイーツ ジェラート」「スイーツ バームクーヘン」「スイーツ どら焼き」「スイーツ たい焼き」「スイーツ 羊羹」の5つのキーワードの組み合わせで検索した結果。それぞれリツイートを省いた20件を収集する。

収集はTwitter APIを使用して以下のPython3コードで行う。認証ライブラリのrequests_oauthlibを使用。具体的な方法は、requests_oauthlibとStreaming APIでリアルタイムにツイートを取得したRaspberry PiとPython3とTwitterでOCRボットを作るを参照。

リツイートを省くので多めに検索して各キーワードの組ごとに20件を収集。さらに、ユーザー名とURLを削除し、解析前に行うことが望ましい文字列の正規化処理にあるPythonコードをそのまま使わせてもらって正規化も行う。この正規化のPythonコードはnormalize_neologd.pyというファイルに保存しておく。

import json
from requests_oauthlib import OAuth1Session, OAuth1
import codecs
import re
from normalize_neologd import normalize_neologd

# Twitter APIの認証情報
# Twitterの開発者向けのページで取得したキーとトークンを使う
CONSUMER_KEY = 'Consumer Key'
CONSUMER_SECRET = 'Consumer Secret'
ACCESS_TOKEN = 'Access Token'
ACCESS_TOKEN_SECRET = 'Access Token Secret'

# 認証情報の設定
tw = OAuth1Session(CONSUMER_KEY,
                client_secret=CONSUMER_SECRET,
                resource_owner_key=ACCESS_TOKEN,
                resource_owner_secret=ACCESS_TOKEN_SECRET)

def normalize(text):
    """
    テキストを整形する
    """
    text = text.strip()
    # urlの削除
    pat_url = re.compile('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
    text = pat_url.sub('', text)
    # ユーザー名の削除
    pat_user = re.compile('@.*? ')
    text = pat_user.sub('', text)
    # 正規化を行う
    return normalize_neologd(text)

def main():
    url = 'https://api.twitter.com/1.1/search/tweets.json'
    # 検索キーワード
    kword = ['シュークリーム', 'ジェラート', 'バームクーヘン', 'どら焼き', 'たい焼き', '羊羹']

    tweet = []
    for k in kword:
        # リツイートを省くのでcountを多めに設定してツイートを検索
        params = {'q': 'スイーツ ' + k, 'count': 70}
        res = tw.get(url, params = params)

        if res.status_code == 200:
            cnt = 0
            text = ''
            for status in json.loads(res.text)['statuses']:
                # リツイートは収集しない
                pat = re.compile('\ART @.*? ')
                match = pat.match(status['text'])
                if match: continue

                text += normalize(status['text'])
                cnt += 1
                if cnt == 20: break

            if cnt < 20:
             # リツイートを省いたツイートが20件に達しなかったら終了する
                print('Not enough tweet: ', cnt)
                exit()
            tweet.append([k, text])

        else:
            print('Status Code = ', res.status_code)

    # SHIFT-JISでエンコードできない文字があるとUnicodeEncodeErrorになってしまう
    # ので、'ignore'を設定してエラーを無視する
    with codecs.open('sweets.txt', 'w', 'shift_jis', 'ignore') as f:
        # KH Coderで読み込むためにH1タグを使ったテキストを出力する
        for text in tweet:
            f.write('<h1>' + text[0] + '</h1>\n')
            f.write(text[1] + '\n')

if __name__ == '__main__':
    main()

上記コードのファイルsearch_sweets.pyとnormalize_neologd.pyを同じディレクトリに置いて、Python3でsearch_sweets.pyを実行すると、sweets.txtというファイルに結果が保存される。KH Coderで読み込むために文字コードはSHIFT-JISで、分析のときのラベルとなる検索ワードをH1タグで囲っている。ちゃんと収集できていれば、H1タグで囲まれた検索ワードの後に収集したツイートが続くブロックが6個できる。

手抜き感のあるコードだけど、今回のメインはここではないので、とりあえずツイートが収集できればよしとする。


分析の準備


KH Coderを起動して、メニューの[プロジェクト]>[新規]を開いてsweets.txtを読み込む。形態素解析エンジンとして、KH Coderで新語に対応するためにmecab-ipadic-NEologdを使うで辞書をmecab-ipadic-NEologdに変更したMeCabを選択。

sweets.txtを読み込んだら、メニューから[前処理]>[前処理の実行]を選んで前処理を実行する。


前処理が完了したら、ツイッターで検索するときに使用したキーワードを分析対象から外す。「スイーツ」はすべてのテキストに含まれているので分析対象にしても分類には役立たないだろうし、スイーツの名称は、名称自体が分析に影響を与えるのを防ぐため。メニューから[前処理]>[語の取捨選択]を開き、以下のように使用しない語を入力する。


文章のクラスター分析


準備ができたら文章のクラスター分析を行う。メニューから[ツール]>[文章]>[クラスター分析]を開き、「集計単位」をH1に、「クラスター数」を2に変更して「OK」を押す。クラスター数を2にしたのは、洋スイーツと和スイーツに分類されるかなという期待から。

クラスター分析が完了すると以下のようなウィンドウが表示される。続いて「各クラスターに含まれる文書」の「プロット」を押してデンドログラムを表示する。

クラスター分析の結果、以下のようにツイートが分類された。洋スイーツと和スイーツに分類されるという予想は見事に外れた。

デンドログラムを見てもどういう分類がされたのかわからないので、分析するテキストの分量が少なすぎたのかと思ったが、特徴語を見て納得。分析結果のウィンドウで分類されたクラスターごとの特徴語を確認できる。

「バームクーヘン」「シュークリーム」「どら焼き」「たい焼き」を含むクラスター1の特徴語を確認すると、「生地」という語が上位にある。つまり、生地のあるなしで分類されたようだ。確かに「ジェラート」と「羊羹」には生地がない。

予想外の分類がされていて、なかなか面白い結果になった。次はもっと大量のツイートを分析してみたいが、Twitter APIのレート制限には気をつけないと。。

0 件のコメント:

コメントを投稿