Python小ネタシリーズ。

  • Pythonネタのときは基本的にPython3を想定しています。
  • また >>> はインタプリタ実行だけど、入り混じってます。適当にどうぞ。

今回はcollectionsというモジュール、defaultdictCounterについて。ChainMapなんていうのもあるけどお手軽に使えるやつをということで。

 

通常のディクショナリは、存在しないキーを参照しようとした場合、KeyErrorが発生する
※代入しようとした場合、それは初期化になるのでエラーにならない

>>> d_normal = {}
>>> d_normal["noexist"] += 1
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'noexist'

defaultdictの仕組みを使うと、例えばint型の値を使うディクショナリ(風のdefaultdictオブジェクト)を使うことで存在しないキーをいきなり使うことができる(存在しない時のデフォルト値が、型によって補填される。intなら0。)

>>> from collections import defaultdict
>>> d_default = defaultdict(int)
>>> d_default["noexist"] += 1
>>> d_default["noexist"]
1

これを使うと、例えばこんなデータについて

apple
orange
apple
orange
orange
grape
apple
grape

それぞれの個数を調べる、なんていう事がしたいような時、こんなディクショナリを作りたいよねと。

{
  "apple": 3,
  "orange": 5,
  "grape": 2
}

下準備。まずリストにする。

data = """apple
orange
apple
orange
orange
grape
apple
grape""".splitlines()

空のディクショナリを作って・・・

fruits = {}

通常ならこんな風にキーがなかったら0をセットする

for fruit in data:
    # もしディクショナリ内に既に存在すれば
    if fruit in fruits.keys():
        # 1個追加する
        fruits[fruit] += 1
    # もしディクショナリ内に存在しなければ
    else:
        # 1個目を入れる
        fruits[fruit] += 1

>>> fruits
{'apple': 3, 'orange': 3, 'grape': 2}

これを、defaultdictを使うと、こんなにもシンプルに書ける。

fruits = defaultdict(int)
for fruit in data:
    fruits[fruit] += 1

>>> fruits
defaultdict(<class 'int'>, {'apple': 3, 'orange': 3, 'grape': 2})

しかしこれくらい単純な個数計算であれば、同じcollectionsモジュールにCounterというものが用意されていて、リストぶちこむだけで同じ事が実現できてしまう。

>>> from collections import Counter
>>> counts = Counter(data)
>>> counts
Counter({'apple': 3, 'orange': 3, 'grape': 2})

Counterオブジェクトのほうが他にも機能があり、例えばトップ〇のキー/ペアを取得するmost_commonなんていうメソッドがある。

>>> counts.most_common(2)
[('apple', 3), ('orange', 3)]

また、Counterクラスのオブジェクトはdictクラスの子クラスなので、dictが備えている機能は備えている。

Help on class Counter in module collections:

class Counter(builtins.dict)
 |  Dict subclass for counting hashable items.  Sometimes called a bag
 |  or multiset.  Elements are stored as dictionary keys and their counts
 |  are stored as dictionary values.

通常のディクショナリのように値を取り出せるし、

>>> counts["apple"]
3

値だけ取り出したり。

>>> counts.values()
dict_values([3, 3, 2])

キーだけ取り出したり。

>>> counts.keys()
dict_keys(['apple', 'orange', 'grape'])

Counterクラスは演算子オーバーロードもされているので、オブジェクト同士を足したりなんだりができる。

>>> c1 = Counter("aaaaabbbbb")
>>> c1
Counter({'a': 5, 'b': 5})
>>> c2 = Counter("bbbbbccccc")
>>> c2
Counter({'b': 5, 'c': 5})
>>> c1 + c2
Counter({'b': 10, 'a': 5, 'c': 5})
>>> 
>>> c1 - c2
Counter({'a': 5})
>>>

 

結論:べんり。