単語ごとの出現頻度を数える
日本語を処理するときに、文章(テキストデータ)を、単語に分割し品詞情報を付与すると、いろいろ面白いことができます。
たとえば、単語の出現回数のカウントをしたり、出現頻度を計算してみたり ですかね。
ということで。
今回は、それをやってみようかと思います。
まずは単純に単語をカウントしてみる
単純に文章に登場する単語をカウントします。
文章は「私は好きで、よく山に登る。山の空気はおいしいし、山の眺めも好きだから。」にします。
この文章を単語に分割して「名詞」「動詞」だけを取り出して出現回数を数えます。
カウントには、Pythonの辞書を使います。
ソースコードは、こんな感じになります。
# -*- coding: utf-8 -*- from janome.tokenizer import Tokenizer import re from collections import defaultdict # 正規表現 pat = r'名詞|動詞' regex = re.compile(pat) countdic = defaultdict(int) t = Tokenizer() malist = t.tokenize("私は好きで、よく山に登る。山の空気はおいしいし、山の眺めも好きだから。") for n in malist: if(regex.match(n.part_of_speech)): countdic[n.surface] += 1 sorteddic = dict(sorted(countdic.items(), key=lambda x: x[1], reverse=True)) for key, val in zip(sorteddic.keys(), sorteddic.values()): print(key, ":", val)
実行した結果はこちらになります。
辞書を値の降順でソートしています。
山 : 3
好き : 2
私 : 1
登る : 1
空気 : 1
眺め : 1
単語の出現頻度を計算してみる
単語の出現回数だけじゃなくて、全体でいくつの単語の中に、どの程度の割合で出現しているのか?という情報がほしくなるときも、まま、あります。
たしかに、1万語の中で10回でてくるのと、100語の文章で10回でてくるのでは意味合いが全く違いますからね。
なので、今度は、単語の出現回数を、全単語数で割って、出現率みたいなものを追加計算してみます。
上記のソースに「total_words」という全体カウンターを追加して最後に除算してみます。
# -*- coding: utf-8 -*- from janome.tokenizer import Tokenizer import re from collections import defaultdict # 正規表現 pat = r'名詞|動詞' regex = re.compile(pat) total_words = 0 countdic = defaultdict(int) t = Tokenizer() malist = t.tokenize("私は好きで、よく山に登る。山の空気はおいしいし、山の眺めも好きだから。") for n in malist: if(regex.match(n.part_of_speech)): countdic[n.surface] += 1 total_words += 1 sorteddic = dict(sorted(countdic.items(), key=lambda x: x[1], reverse=True)) for key, val in zip(sorteddic.keys(), sorteddic.values()): print(key, ":", val, ":", round(val / total_words, 2))
その結果、以下のようになります。
山 : 3 : 0.33
好き : 2 : 0.22
私 : 1 : 0.11
登る : 1 : 0.11
空気 : 1 : 0.11
眺め : 1 : 0.11
こn出現頻度を使えば、なんとなく文章の分類なんかができそうです。
実際、ネガティブな単語、ポジティブな単語みたいな辞書を作って、どちらの出現頻度が高いかで、文章の簡単なネガポジ判定をしたりとか・・。
まあ、その辞書作成が大変なのですが(笑)
単語の重要度とかも考える
実は単語の出現頻度だけに着目していると、すぐ限界がきます。
複数の文章で単語の出現頻度が同じでも、複数の文章で同じように出現している単語と、特定の文章の中でだけ出現頻度が高い単語があったりするからです。
単語の出現頻度に着目して、文章の識別などをする際に、「文章を特徴づける重要な単語」なのか、「どんな文章でも出現頻度の高い単語」なのかで全然意味合いが違うことくらいは、さすがに、自分でもわかります。
その区別をつけるひとつの方法が、TF-IDFです。
TF(Term Frequency)が単語の出現頻度です。
IDF(Inverse Document Frequency)は「特定の文書に出現する単語ほど,ある話題に特化した意味のある単語である」という考えで求める値です。.
TF-IDFは、上記2つの値の積で表されます.
TF-IDFは、同じ出現回数でもあちこちの文書に頻繁にでてくる場合は値が小さくなり、逆の場合は大きくなります。
このへんになると、機械学習に使うデータがどうの・・みたいな難しい話になってしまうことが多いのですけど、ここでは、雰囲気だけやってみます。
この計算は、scikit-learnというpythonのライブラリを使うと簡単です。
インストールは。
pip install scikit-learn
TF-IDFを計算してみる
scikit-learnで文章を処理するには、日本語の文章を空白で分かちがちした形にしてやる必要があります。
単語に分割できれば、それを空白を間にはさんで文字列に直せばいいだけなので、そこはやった・・というていで、やります。
サンプルに不要な助詞とかをはぶいて半角空白をはさんだ、以下の文章を使います。
- 私 山 好き 私 山 空気 おいしい 私 山 景色 きれい
- 私 読書 趣味 私 映画 好き 私 演劇 見る 好き
- 私 スポーツ 苦手 私 スポーツ 見る 好き 私 山 登る 好き
これらのに含まれる単語のどれが、その文章を特徴づける重要な単語なのか?を計算するプログラムはこんな感じになります。
from sklearn.feature_extraction.text import TfidfVectorizer input_list = [ "私 山 好き 私 山 空気 おいしい 私 山 景色 きれい", "私 読書 趣味 私 映画 好き 私 演劇 見る 好き", "私 スポーツ 苦手 私 スポーツ 見る 好き 私 山 登る 好き" ] vectorizer = TfidfVectorizer() X = vectorizer.fit_transform(input_list) names = vectorizer.get_feature_names() results = X.toarray() for index, val in enumerate(names): print( val, ":", results[0][index], ":", results[1][index], ":", results[2][index])
これを実行した結果がこうです。
おいしい : 0.479527938028855 : 0.0 : 0.0
きれい : 0.479527938028855 : 0.0 : 0.0
スポーツ : 0.0 : 0.0 : 0.7082715615424613
好き : 0.2832169249871526 : 0.48329605728496866 : 0.41831659389954917
映画 : 0.0 : 0.40914567838389126 : 0.0
景色 : 0.479527938028855 : 0.0 : 0.0
演劇 : 0.0 : 0.40914567838389126 : 0.0
登る : 0.0 : 0.0 : 0.35413578077123065
空気 : 0.479527938028855 : 0.0 : 0.0
苦手 : 0.0 : 0.0 : 0.35413578077123065
見る : 0.0 : 0.3111658343262415 : 0.2693293892867707
読書 : 0.0 : 0.40914567838389126 : 0.0
趣味 : 0.0 : 0.40914567838389126 : 0.0
これを見ると。
「おいしい」「景色」「空気」あたりが、最初の文章「私 山 好き 私 山 空気 おいしい 私 山 景色 きれい」を特徴づける重要な単語であることがわかります。
同様に「映画」「演劇」「読書」「趣味」あたりが、2番目の「私 読書 趣味 私 映画 好き 私 演劇 見る 好き」を特徴できる単語であることもわかります。
わざとらしく、たくさん登場させた「私」という単語はありませんね。
どれにもまんべんなく登場しているので、それぞれの文章を特徴づける単語としては重要でないと判断されたのでしょうか?
なんにせよ。
この情報が、文章の分類などの用途で役に立つだろうなということは、さすがに、自分でもわかりますしね。
なかなか、なかなか興味深いです。
ではでは。