テキストデータ中の単語の出現回数を数える

テキストが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 を使うオプションなどはなさそうです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です