テキストが1個だけなら、scikit-lerarnでBoW作って終わりなので、テキストデータは複数あるものとします。
(とは言っても、やることは各テキストをBoWにして足すだけです。)
結果を全部列挙したらすごい量になるのと、数えるだけだと面白くないので、
アウトプットは出現回数が多い単語のランキングにしましょう。
今回もサンプルデータは20newsgroupsを使わせていただきます。
また、この記事では出現回数数えて、上位の単語を列挙するところををゴールとし、
機械学習にかけたりしないのでstop_wordsや、細かな前処理は省きます。
コードは以下のようになりました。
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.datasets import fetch_20newsgroups
import numpy as np
# データの読み込み
remove = ('headers', 'footers', 'quotes')
twenty_news = fetch_20newsgroups(
subset='all',
remove=remove,
)
X = twenty_news.data
# 文章の数
print(len(X)) # 18846
# BoW化(今回は出現頻度が高い単語に関心があるので、min_dfは大きめに設定。
tf_vectorizer = CountVectorizer(
min_df=50,
token_pattern='(?u)\\b\\w+\\b', # 1文字の単語も対象とする
)
tf = tf_vectorizer.fit_transform(X)
# モデルが集計対象とした(min_df回以上出現した)単語の数
print(len(tf_vectorizer.get_feature_names())) # 4476
print(tf.shape) # (18846, 4476)
# 単語ごとに各ドキュメントの出現回数を足し合わせる
term_frequency = np.array(tf.sum(axis=0))[0]
print(term_frequency.shape) # (4476,)
# 出現回数上位100位までの単語と出現回数を表示
for i in term_frequency.argsort()[:-100:-1]:
print(term_frequency[i], "\t", tf_vectorizer.get_feature_names()[i])
# 以下出力
'''
173592 the
86907 to
77192 of
73928 a
70083 and
59683 i
50898 in
48899 is
45942 that
38660 it
32302 for
30492 you
24449 s
23617 on
23519 this
22030 be
-- (以下略) --
'''
不要語の除去をやっていないので、当然のように冠詞や前置詞など超汎用的な単語が並びました。
最後の出力には以前の記事で紹介したargsortを使っています。
参考:numpyのarrayを並び替えた結果をインデックスで取得する
[:-100:-1]
というスライスで、最後の100項を逆順にとっているのもポイント。
単語ごとに各ドキュメントの出現回数を足し合わせるところでは、少しだけ工夫をしました。
まず、BoWが格納されている変数tf
ですが、 csr_matrix というデータ型になっています。
これは疎行列というメモリを節約できて、うまく使えば計算時間も短縮できる便利なものなのですが、
一見わかりにくいので toarray()
でarray型に変化してしまってる例をよく見かけます。
今回のコードでは、np.array(tf.sum(axis=0))[0]
という風に、
先にsumした後に、arrayに変換して、1次元に変換しました。
こうすると、toarray()してから和を取るよりもかなり早く処理できます。
jupyterなら %timeit で手軽に時間を測れるので比較しましょう。
%timeit tf.toarray().sum(axis=0)
# 355 ms ± 5.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.array(tf.sum(axis=0))[0]
# 1.47 ms ± 75.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
240倍も違いますね。
列の和だけなら、 csr_matrix より csc_matrix の方が計算が早いと思うのですが、
変換にかかる時間もありますし、十分早いのでこれで良いでしょう。
scikit-learnのソースコードも確認しましたが、
csr_matrix で結果を返すようになっていて、
csc_matrix を使うオプションなどはなさそうです。