競馬予想コメントをクラスタリングしWordCloudで可視化する

目的

前回Scrapyを利用して取得した競馬予想家のコメントを分析して、競馬予想の着眼点を探してみる。

準備

今回は自然言語解析を行うため、形態素解析エンジンMeCabをインストールする。

!pip install mecab-python3 unidic-lite

続いて、あとで利用するWordCloudと日本語フォントをインストールする。

!pip install wordcloud japanize_matplotlib

分析

今回はおおまかに下記の手順で行った。

  1. MeCabでコメントを分かち書きにする。
  2. tf-idfでコメントをベクトル化する。
  3. K-Means法でクラスタリングする。
  4. 各クラスタをWordCloudで可視化する。

まずは前回Scrapyを利用して取得した競馬予想家のコメントを読み込む。

import pandas as pd

df = pd.read_csv('./comment.csv')
df = df.dropna(subset=['comment'])

# 回避馬が発生した際のコメントを消す。
pattern = '.*回避馬が含まれるケースがございます。あらかじめご了承ください。'
df['comment'] = df['comment'].str.replace(pattern, '', regex=True)
df['comment'] = df['comment'].str.replace('<br>', '', regex=True)

各コメントを分かち書きにする。
分かち書きとは、文章において語の区切りに空白を挟んで記述することである。

def get_stop_words():
    url = "http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt"
    r = requests.get(url)
    tmp = r.text.split('\r\n')
    stop_words = []
    for i in range(len(tmp)):
        if len(tmp[i]) < 1:
            continue
        stop_words.append(tmp[i])
    stop_words = stop_words + ['が', 'で', 'は', 'の', 'も', 'を', 'て', 'に', 'だ', 
                               'br', 'と', 'た', 'や', 'ます', 'など', 'あり', 'する', 'ある', 
                               'な', 'き', 'いる', 'から', 'そう', 'し', 'おり', 'ば', 'なら',
                               'いう', 'れ', 'かつ', 'か', 'ない', 'です']
    return stop_words


def parse_comment(comment, tagger=None, stop_words=None):
    try:
        lines = tagger.parse(comment).split('\n')
    except TypeError:
        print(comment)
    except AttributeError:
        print(comment)
        
    word_list = []
    for line in lines:
        if line == 'EOS':
            break
        if line in stop_words:
            continue
        word = line.split('\t')[0]
        word_list.append(word)
    return ' '.join(word_list)

TfidfVectorizerで各コメントをベクトル化する。
tf-idfは、文書中に含まれる単語の重要度を評価する手法である。
これが後の工程で行うクラスタリングに効くと思われる。

from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['wakati'])

ベクトル化により、各コメントが29,629次元のベクトルになった。

print(X.shape)
(44642, 29629)

K-Means法でクラスタリングする。クラスタ数kはエルボー法で決める。
エルボー法を行う中でK-Means法だと計算時間がかかりすぎたので、Mini-Batch K-Means法を利用した。

from sklearn.cluster import MiniBatchKMeans
import tqdm

distortions = []
num = 50

for i in tqdm.tqdm(range(1,num+1)):
    model = MiniBatchKMeans(n_clusters=i,
                             init='k-means++',
                             n_init=10,
                             max_iter=300,
                             random_state=42,
                             batch_size=1024)
    
    model.fit(feature)

    distortions.append(model.inertia_)

plt.figure(figsize=(10, 10))
plt.title("エルボー法")
plt.plot(
    range(1,num+1),
    distortions,
    marker='o'
    )

plt.xlabel('クラスター数')
plt.xticks( np.arange(0, num, 2))
plt.ylabel('歪み')
plt.show()

diff = []
for i in range(1, len(distortions)):
    diff.append(distortions[i-1]-distortions[i])
plt.plot(
    range(1,len(diff)+1),
    diff,
    marker='o'
    )
plt.xticks( np.arange(0, num, 2))
plt.show()

クラスタ数を7にして、K-Means法でクラスタリングする。

from sklearn.cluster import KMeans

CLUSTRE_NUM = 7
km_model = KMeans(n_clusters=CLUSTRE_NUM)
km_model.fit(X)
df['label'] = km_model.labels_

各クラスタのコメントをまとめて、ワードクラウドにしてみる。

from wordcloud import WordCloud
import japanize_matplotlib
import matplotlib.pyplot as plt

for label in df['label'].unique():
    cloud_text = ' '.join(df[df['label']==label]['wakati'].values.tolist())
    word_cloud = WordCloud(stopwords=set(stop_words),
                           collocations = False,
                           font_path=japanize_matplotlib.get_font_ttf_path()).generate(cloud_text)
    plt.imshow(word_cloud)
    plt.axis('off')
    plt.show()

結果

予想家がコメントによく含める単語がワードクラウドに現れる。
これが競馬予想の着眼点になるはず。

左上からみていくと、「対抗」「主力」「次位」「チャンス」「優勢」と着順に関して単語が多い。
「能力」という単語は 潜在能力 として使われており、血統の意味と思われる。
「先行」「末脚」など 脚質に関する単語も多い。

次の表については「波乱」はレース展開について使われているよう。着眼点というよりは波乱度という予測対象にすると面白そう。
「前進」は前進気勢といった、前に進みたい気持ちや勢いを表すよう。馬のメンタルを意味してそう。
「アップ」「レース」は着眼点にはならなさそう。

3番目の表については「前走」「初戦」は前走・初戦の成績について言及しているよう。
「上位」は予想順位が上位かどうかの意味で、着眼点には役立たない。

4番目の表については「相手」「上位」「主体」「評価」「組み合わせ」など馬券の買い方についてが多い。
複数頭の馬を比較するというのを着眼点として解釈できるかもしれない。

5番目の表については「上位」「勝負」「注意」など着順・馬券の買い方についてが多い。
「前走」「2着」で前走の着順が着眼点であることがうかがえる。

6番目の表については「値」は平均値や前走値の意味で使われていた。
「前走」「同距離」「コース」は前走で同距離・同コースを走ったかどうかとして解釈できる。

7番目の表については「前走」「距離」「馬場」「2着」「ダート」から見えるのは、着眼点として同じ距離・コンディションで走った経験あるかということだと思う。
「馬」「レース」はあまり意味なさそう。

まとめ

競馬予想家のコメントを分析して、競馬予想の着眼点を探してみた。

次の着眼点が見つかった。

  • 馬の血統
  • 脚質
  • 馬のメンタル
  • 複数頭を比較
  • 前走・初戦の成績
  • 同じ距離・コンディションで走った経験あるか