2018年2月28日水曜日

Python3でツイートのクラスター分析をする

KH Coderでツイートを分類するでツイートをKH Coderを使ってクラスタリングしてみたが、今度はPython3でやってみる。KH Coderでツイートを分類するで「スイーツ シュークリーム」「スイーツ ジェラート」「スイーツ バームクーヘン」「スイーツ どら焼き」「スイーツ たい焼き」「スイーツ 羊羹」を検索ワードとして収集したツイートを使う。mecab-ipadic-NEologdで形態素解析を新語に対応させるでインストールしたMeCabとmecab-ipadic-NEologdで形態素解析を行い、各スイーツのツイートをTF-IDFでベクトル化する。そして、ベクトル化した文章間の距離をもとにクラスタリングし、最後にデンドログラムを作成する。


環境


Raspberry PiをJupyter Notebookサーバにするの手順でBash on Ubuntu on Windowsに構築したJupyter Notebookサーバ。



Pythonライブラリ等のインストール


必要なPythonライブラリをインストールする。


続いて、PythonでMeCabとmecab-ipadic-NEologdを使える環境にしておく(mecab-ipadic-NEologdで形態素解析を新語に対応させるを参照)。

さらに、matplotlibで日本語を使えるようにしておく(Raspberry PiとPython3でグラフを作成する参照)。


ツイートテキストの読み込み


KH Coderでツイートを分類するで収集したツイートのテキストsweets.txt(スイーツ名がH1タグで囲まれている)から、スイーツ名をインデックスとするPnadasのデータフレームを作成する。

import codecs
import re
import pandas as pd

def get_sweets():
    with codecs.open('sweets2.txt', 'r', 'shift_jis', 'ignore') as f:
        lines = f.readlines()

    pat = re.compile('

(.+)

') cat = '' sweets = {} for line in lines: match = pat.match(line) if match: cat = match.group(1) sweets[cat] = '' else: sweets[cat] += line # スイーツ名をインデックスにしてDataframe作成 return pd.DataFrame.from_dict(sweets, orient='index') def main(): df = get_sweets() df.index.name = 'スイーツ名' df.columns = ['ツイート'] if __name__ == '__main__': main()

作成したデータフレームは以下の通り。



TF-IDFでベクトル化する


MeCabでツイートを分かち書きして、scikit-learnのTfidfVectorizerでTF-IDFを計算する。今回は「名詞」「動詞」「形容詞」「副詞」「感動詞」のみを使う。さらに、ツイート収集時の検索ワードは使用しない。
import codecs
import re
import pandas as pd
import MeCab
from sklearn.feature_extraction.text import TfidfVectorizer

def wakati(text):
    """
    対象とする品詞のみでわかち書き
    """
    # 取り込む品詞
    cat_list = ['名詞', '動詞', '形容詞', '副詞', '感動詞']

    # ストップワード
    stop_list = ['スイーツ', '羊羹', 'ジェラート', 'どら焼き', 'シュークリーム', 'バームクーヘン', 'たい焼き']

    # 辞書としてmecab-ipadic-neologdを使う
    m = MeCab.Tagger('-Ochasen -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')
    m.parse('')
    node = m.parseToNode(text)
    wd_list = []
    while node:
        cat = node.feature.split(',')[0]
        if cat in cat_list and node.surface not in stop_list:
            wd_list.append(node.surface)

        node = node.next

    return ' '.join(wd_list)

def calc_tfidf(wakati_list):
    """
    TF-IDFでベクトル化
    """
    # token_pattern=u'(?u)\\b\\w+\\b : 文字列長が 1 の単語を処理対象に含める
    vectorizer = TfidfVectorizer(token_pattern='(?u)\\b\\w+\\b')
    vecs = vectorizer.fit_transform(wakati_list)

    print('TF-IDF=',)
    print(vecs.toarray().shape)
    print(vecs.toarray())

    return vecs

def main():
    df = get_sweets()
    df.index.name = 'スイーツ名'
    df.columns = ['ツイート']

    # ツイートをわかち書きにする
    df['wakati'] = df['ツイート'].apply(wakati)

    # TF-IDF計算
    vecs = calc_tfidf(df['wakati'].tolist())

if __name__ == '__main__':
    main()

以下のようなTF-IDFが得られる。行は各スイーツのツイート、列は語。



デンドログラムの作成


最後にscipyのlinkagedendrogramでデンドログラムを作成する。ここではコサイン距離を使ってクラスタリングする。
import codecs
import re
import pandas as pd
import MeCab
from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.cluster.hierarchy import dendrogram, linkage

import matplotlib
%matplotlib inline
import matplotlib.pyplot as plt

# matplotlibの日本語フォント指定
font = {'family' : 'IPAexGothic'}
matplotlib.rc('font', **font)

def gen_cluster(X, labels):
    # コサイン距離を使ってクラスタリングする
    Z = linkage(X, metric='cosine')

    # デンドログラムの作成
    plt.figure(figsize=(10, 5))
    plt.ylabel('距離')
    dendrogram(
        Z,
        leaf_rotation=90.,
        leaf_font_size=12.,
        labels=labels
    )
    plt.show()

def main():
    df = get_sweets()
    df.index.name = 'スイーツ名'
    df.columns = ['ツイート']

    # ツイートをわかち書きにする
    df['wakati'] = df['ツイート'].apply(wakati)

    # TF-IDF計算
    vecs = calc_tfidf(df['wakati'].tolist())

    # デンドログラムの作成
    gen_cluster(vecs.toarray(), df.index.values)
 
if __name__ == '__main__':
    main()


結果


以下のようなデンドログラムが作成された。「たい焼き」「どら焼き」と「シュークリーム」「バームクーヘン」がそれぞれ近くにあって、直感に近い分類になった。

KH Coderでツイートを分類するで作成したKH Coderによるデンドログラムとは違う結果になったけれど、クラスタリングの方法や距離が違うので単純な比較はできない。

0 件のコメント:

コメントを投稿