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のデータフレームを作成する。

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
26
27
28
29
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('<h1>(.+)</h1>')
    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を計算する。今回は「名詞」「動詞」「形容詞」「副詞」「感動詞」のみを使う。さらに、ツイート収集時の検索ワードは使用しない。
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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でデンドログラムを作成する。ここではコサイン距離を使ってクラスタリングする。
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 件のコメント:

コメントを投稿