u++の備忘録

TF-IDFを用いた「Kaggle流行語大賞2018」【kaggle Advent Calendar 14日目】

本記事は、kaggle Advent Calendar 2018の14日目の記事です。12日目で最後の予定でしたが、穴が空いていたので2日ぶり6回目の投稿です。

qiita.com

はじめに

本記事では、年の瀬ということで「Kaggle流行語大賞2018」という題材に取り組みます。

具体的には、今年に公開された全てのKernelのタイトルを収集し、単語ごとの登場回数を計算しました。冠詞や代名詞などの一般的な単語を除外し、さらにTF-IDFを用いて2018年に特に多く登場した単語を特定することで、栄えある「Kaggle流行語大賞2018」を決定したいと思います。

データ収集

今回は「Meta Kaggle」という、Kaggle公式が1日ごとにデータを更新しているメタ情報を利用します。最新のデータが12月12日の時点のcsvをダウンロードしました。

www.kaggle.com

"Kernels.csv" 内に、Kernelの公開日・URLなどの情報が格納されていました。

KaggleのURLは、最後の/以下が「Kernelのタイトルを全て小文字に変換し、半角スペースをハイフンで置換した文字列」になっています。下図のように「Simple lightGBM KFold」というタイトルの場合は「simple-lightgbm-kfold」です。

f:id:upura:20181213175223p:plain

全てが小文字に変換されている点は、集計を考える上で非常に都合が良いです。例えば「lightGBM」と「LightGBM」のような表記ブレを気にすることなく単語の登場回数を計算できます。今回は、このURLを利用して分析を進めることにします。

ちなみに、Kaggle APIの利用も検討しました。

github.com

しかし、下記のようなシェルスクリプトでデータを取得しようとしたところ、数日程度前までのデータまでしか遡ることができませんでした。

for i in `seq 1 100`
do
  kaggle kernels list --sort-by 'dateCreated' -v -p $i >> data.csv
done

データの加工

最初に、対象とするKernelを「Vote数が0より大きい」という条件で絞り込みます。Vote数が0のKernelを取り除くことで、ある程度の質を担保したいという意図があります。

kernel = kernel[kernel['TotalVotes'] > 0]

あとは年ごとに、タイトルに含まれる単語をリストに格納していきます。

kernel['Date'] = pd.to_datetime(kernel['MadePublicDate'])

kernel2018 =  kernel[kernel['Date'].dt.year == 2018.0].reset_index()
words2018 = []

for _ in (kernel2018['CurrentUrlSlug']):
    words2018 += _.split("-")

上記を実行することで、word2018というリストに、2018年に公開された全てのKernelのタイトルに含まれる単語がまとめて格納されます。

例えば以下の3つのタイトルのKernelがあった場合、word2018は次のようになります。2つのKernelで登場する"analysis"はリスト内に2回登場しています。

  • This is my first analysis
  • Wonderful analysis one
  • hello world
word2018 = [ "this", "is", "my", "first", "analysis", "wonderful", "analysis", "one",  "hello", "world"]

比較のため、同様に2017年、2016年、2015年分のリストも作成しました。

単語の登場回数

最初に、単純にリスト内の登場回数を見てみます。出力結果は、上位25件を抜粋しました。

import collections
c2018 = collections.Counter(words2018)
c2018.most_common()
[('data', 2749),
 ('and', 1691),
 ('with', 1614),
 ('analysis', 1413),
 ('of', 1187),
 ('eda', 1151),
 ('for', 872),
 ('titanic', 814),
 ('challenge', 812),
 ('in', 796),
 ('using', 786),
 ('to', 749),
 ('the', 742),
 ('prediction', 736),
 ('cleaning', 654),
 ('day', 647),
 ('model', 624),
 ('1', 620),
 ('learning', 603),
 ('on', 537),
 ('0', 536),
 ('a', 528),
 ('regression', 517),
 ('simple', 511),
 ('keras', 504),

"data"や"analysis"といったKaggleの文脈では当たり前の単語や、"and", "with", "of"などの自然言語処理の世界ではStop wordsと呼ばれるような単語が上位に来ています。

また同様に2017年版の上位25件を見てみると、似たような顔ぶれが登場しているような印象を受けます。単純な登場回数の上位を「流行語」と評価するのは、あまり適切ではなさそうです。

[('data', 2047),
 ('analysis', 1131),
 ('titanic', 1102),
 ('and', 1098),
 ('with', 1020),
 ('of', 957),
 ('day', 786),
 ('the', 713),
 ('0', 709),
 ('in', 698),
 ('to', 603),
 ('for', 538),
 ('eda', 520),
 ('regression', 496),
 ('challenge', 488),
 ('a', 483),
 ('using', 481),
 ('prediction', 432),
 ('on', 431),
 ('lb', 421),
 ('science', 415),
 ('python', 413),
 ('5', 396),
 ('simple', 394),
 ('r', 392),
 ('exploration', 375),

TF-IDFの計算

上記の考察を踏まえて「Kaggle流行語大賞2018」を決めるに当たって、以下の2つの処理を実施します。

  • "data"や"analysis"といったKaggleの文脈では当たり前の単語や、"and", "with", "of"などの自然言語処理の世界ではStop wordsと呼ばれるような単語の除去
  • 単純な登場回数ではなく、他の年にあまり出てこない単語を重要視して評価

前者について具体的には、NLTKというパッケージで定義されているStop wordsに加え、自分自身で消去すべきと考えた単語を取り除きます。デフォルトの定義に追加した単語は"data", "analysis", "using"の3つです。

from nltk.corpus import stopwords
stopWords = stopwords.words("english")

stopWords.append('data')
stopWords.append('analysis')
stopWords.append('using')

後者については、まさにこのような考え方で定義された「TF-IDF」を用います。詳細は巷に解説記事が溢れているので割愛しますが、多くのグループで出現する語(一般的な語)は重要度が下がり、特定のグループにしか出現しない単語の重要度は上がるような計算を施すことになります。

ここで、TfidfVectorizerの引数に「stop_words = stopWords」を与え、先に定義したStop wordsを取り除きました。

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words = stopWords)
X = vectorizer.fit_transform(data).toarray()

TF-IDFに基づく「Kaggle流行語大賞2018」

2018年のTF-IDFの値で降順にソートした上位20件を以下に示します。

df = pd.DataFrame(X.T, index = vectorizer.get_feature_names(), columns = ['words2015', 'words2016', 'words2017', 'words2018'])
df.sort_values('words2018', ascending=False).head(20)

f:id:upura:20181213230202p:plain

単純に単語の登場回数を計算した場合と比べ、Stop wordsが除去され、かつ他の年にあまり出てこない単語を重要視した結果となっていると分かります。例えば、3位の"scavenger"という単語は単純な登場回数だけで判断すると430回で30位に相当しますが、2018年になって登場した単語なので、TF-IDFを用いた場合には上位にランクインしています。

ちなみに、"scavenger"は今年に公開されたKaggle内の「SQL Scavenger Hunt Handbook」というコース?の名称の一部だと思われます。

SQL Scavenger Hunt Handbook | Kaggle

この結果を、下の折れ線グラフで示します。

f:id:upura:20181212080323p:plain

栄えある1位には"eda"が輝きました。2016年から登場し始めた単語で、2017年から更に登場回数が増えていきました。探索的データ分析(Exploratory data analysis)という考え方が徐々に浸透しつつあると読み取れるかと思います。

一方で、2017年に1位だった"titanic"は4位に後退しました。2012年に公開されたKaggleの有名なチュートリアルですが、流石に陰りが見え始めてきたということかもしれません。

Titanic: Machine Learning from Disaster | Kaggle

その他気になるところとしては、"cnn"が着実に順位を上げてきています。

おわりに

本記事では、TF-IDFを用いて「Kaggle流行語大賞2018」を選定しました。

2018年に開催されたコンペ名に含まれる単語を除外するなど、まだまだ改善の余地は大いにあると思います(「Santander Value Prediction Challenge」の"challenge"など)。とはいえざっと眺めているだけでも2018年のKaggleの潮流が垣間見えてきて、なかなか興味深い分析結果になったように思いました。

実装はGitHubで公開しました。

github.com