Python: 文章中の単語出現回数をカウントするときに、defaultdictを使うとシンプルに書ける

例として以下の文章内(words)で出現する単語の頻度をカウントします。

words = ‘Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python has a design philosophy that emphasizes code readability, notably using significant whitespace. It provides constructs that enable clear programming on both small and large scales. In July 2018, Van Rossum stepped down as the leader in the language community.’

面倒臭いので 話をシンプルにするため、「,」や「.」、大文字小文字は考えないことにします。
普通に書くとこんな感じでしょうか。

count_word.py

word_count = {}
for word in words.split():
    if word in word_count:
        word_count[word] += 1
    else:
        word_count[word] = 1
print(word_count)

こっちの方がわかりやすいでしょうか。

word_count = {}
for word in words.split():
    if not word in word_count:
        word_count[word] = 0
    word_count[word] += 1
print(word_count)

いずれにしても、word_countディクショナリに存在しないwordが出てきたら、初期化をしています。
得られる結果は以下の通りです。

python count_word.py 
{'and': 2, 'the': 2, 'code': 1, 'Van': 1, '1991,': 1, 'is': 1, 'both': 1, 'an': 1, 'down': 1, 'as': 1, 'design': 1, 'in': 2, 'readability,': 1, 'It': 1, 'van': 1, 'Python': 2, 'Rossum': 2, 'interpreted,': 1, 'that': 2, 'provides': 1, 'July': 1, 'has': 1, 'leader': 1, 'stepped': 1, 'enable': 1, 'Created': 1, 'philosophy': 1, 'constructs': 1, 'emphasizes': 1, 'general-purpose': 1, 'notably': 1, 'released': 1, 'high-level,': 1, 'significant': 1, 'Guido': 1, 'using': 1, 'by': 1, 'a': 1, 'on': 1, 'language': 1, 'whitespace.': 1, 'clear': 1, 'programming': 2, 'language.': 1, 'large': 1, 'community.': 1, 'In': 1, 'small': 1, '2018,': 1, 'scales.': 1, 'first': 1}

初期化が面倒くさいので、以下のように書きたくなってしまいますが、これではエラーになってしまいます。

word_count = {}
for word in words.split():
    word_count[word] += 1
print(word_count)
python count_word.py 
Traceback (most recent call last):
  File "count_word.py", line 7, in 
    word_count[word] += 1
KeyError: 'Python'

一つ目のword「Python」がword_countディクショナリに無いためです。
そこで、defaultdictの出番です。defaultdictを使用することで、あらかじめ初期値を設定することができます。

from collections import defaultdict
word_count = defaultdict(int)
for word in words.split():
    word_count[word] += 1
print(word_count)

defaultdict(int)としていますが、これは初期値を0にするということです。

python count_word.py 
defaultdict(, {'and': 2, 'the': 2, 'code': 1, 'Van': 1, '1991,': 1, 'is': 1, 'both': 1, 'an': 1, 'down': 1, 'as': 1, 'design': 1, 'in': 2, 'readability,': 1, 'It': 1, 'van': 1, 'Python': 2, 'Rossum': 2, 'interpreted,': 1, 'that': 2, 'provides': 1, 'July': 1, 'has': 1, 'leader': 1, 'stepped': 1, 'enable': 1, 'Created': 1, 'philosophy': 1, 'constructs': 1, 'emphasizes': 1, 'general-purpose': 1, 'notably': 1, 'released': 1, 'high-level,': 1, 'significant': 1, 'Guido': 1, 'using': 1, 'by': 1, 'a': 1, 'on': 1, 'language': 1, 'whitespace.': 1, 'clear': 1, 'programming': 2, 'language.': 1, 'large': 1, 'community.': 1, 'In': 1, 'small': 1, '2018,': 1, 'scales.': 1, 'first': 1})

無事結果が得られました。
defaultdictを使用することで、for文の中身がすっきりしてわかりやすくなったかと思います。
ちなみに、以下のような書き方も良い感じです。(あまり見かけない気がしますが…)

word_count = {}
for word in words.split():
    word_count[word] = word_count.get(word, 0) + 1
print(word_count)

ディクショナリのgetメソッドを使用して、第1引数に取得したい要素のキーを、第2引数にキーが存在しない場合のデフォルト値を指定しています。
今回の場合には、collections.Counterを使用するのもいいですが、先に全部リスト化する必要があるので、長い文章になった場合にはメモリが足りなくなる恐れがあることに注意です。

コメントする

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