Pythonの辞書(dictionary)と、拡張する組み込みアルゴリズム(OrderedDict、defaultdict)の基本操作(初期化・追加・一覧・ソート)をまとめます。
辞書型の初期化
空で初期化するだけなら簡単です。
dic={}
一次元で静的に値を与えて初期化するなら。
dic={ 'A': 'VVV1', 'B', 'VVV2' }
のように、「’KEY': 'VALUE'」の組合せで初期値を与えます。
ネストする場合は少々長くなるので、別記事にしています。
データ追加時に「存在しないキー」がある時の扱い
単語の出現頻度を数えるために辞書でカウントアップする例で、やります。
ポイントは・・。
一般的な辞書(dictianary)の場合、単純に「md[key] += 1 」だけすると、存在しないキーでエラーで落ちる(=1件目からエラーになる)ということです。
一般的な辞書(DICT)。
問題回避の方法としては3通り。
- キーの存在チェックをして、なければ先に代入するを明示的にする
- 上記の判断と処理を内部的にやってくれる「setdefault()」を使う
- get()(存在しないキーだと指定したデフォルトを返す)で取得してから加算する
です。
seed = seed = [ 'apple', 'orange', 'banana', 'orange', 'banana', 'apple', 'apple', 'apple'] # キーの存在チェックをする方法 md = {} for key in seed: if key not in md: md[key] = 0 md[key] += 1 # setdefaultを使う方法 md2 = {} for key in seed: md2.setdefault(key, 0) md2[key] += 1 # get()(存在しないキーだと指定したデフォルトを返す)で取得してから加算する md3 = {} for key in seed: md3[key] = md3.get(key, 0) + 1
まあ、これでも良いのですけど、perlとかで連想配列を覚えた人間にとっては、md[key] += 1」だけで、キーがない時は、自動的に0をセットしてほしい・・と思います。
デフォルト辞書
md[key] += 1」だけで、キーがない時は、自動的に0をセットしてくれるのが、デフォルト辞書(defaultdict)です。
from collections import defaultdict seed = ['apple','orange','banana','orange','banana','apple','apple','apple'] md = defaultdict(int) for key in seed: md[key] += 1
違いは初期化の仕方だけです。
① collectionsモジュールのdefaultdictクラスをインポートする。
② defaultdict()で初期化する。
これだけで、上記のようにスマートにカウントできるようになります。
ちなみに、defaultdictクラスの引数のintは、デフォルト値「0」を返す関数です。
変数ではないです。
普通の辞書と同じように使えますが、djangoテンプレートを使うときには直接渡せないなどの注意事項があります。
辞書にセットしたキーや値を参照する
個別にキーを指定して取得する方法と、forループをまわすために一括で取得するか方法をまとめておきます。
個別にキーを指定して取得
配列の引数みたいに個別のキーを指定して値を取得するやり方です。
例えば。
info = {'apple':'りんごだよ','orange':'みかんだよ','banana':'ばななだよ'}
みたいな辞書を初期化で展開しておいたなら。
info['apple']
で「りんごだよ」の文字列を得るみたいな感じです。
文字列をキーにする「配列」というイメージ通りでいけます。
格納されている全てのキーと値を一覧する
キーの一覧(リスト)は、「keys()」で取得できます。
値の一覧(リスト)は、「values()」で取得できます。
キーと値をセットで取得するには「items()」で取得できます。
for key,val in md.items():
print(key,":",val)
みたいな感じですね。
この3パターンがメインです。
ただ、items()と同じことを、「keys()」と「values()」をzip()と組合せてやることもできます。
例は以下です。
例では「デフォルト辞書」を使ってますが、初期化の部分が少し違うだけで、参照の方法は「通常の辞書」でも同じです。
from collections import defaultdict seed = ['orange','banana','apple','apple','apple','apple','orange','banana','banana','banana','banana','banana'] md = defaultdict(int) for key in seed: md[key] += 1 for key,val in zip(md.keys(),md.values()): print(key,":",val)
これで以下のような結果が表示されます。
orange : 2
banana : 6
apple : 4
個人的にはやってることが明確に見えるので、好きな書き方ではあります。
でもまあ。
keys()とvalues()がきちんとペアで取得されることが保証されるのか?とかを気にする方もいるので、特に理由がない限り「items()」を使うのが無難です。
ソートに関する話題
辞書から値を取り出して一覧したりするときのソート(並び順)に関するまとめです。
キーの挿入順の保証
デフォルト辞書の場合は、取り出す順番がキーの挿入順であることが保証されるかされないかは、バージョンに依存します。
Python3.7からは言語仕様で、標準の辞書でも挿入順が保証され、Python3.6でもCPythonの実装ベースで同様の動きをします。
しかし、それ以外だと保証されません。
なので。
バージョンにかかわらず、キーの挿入順を記録して、その順番で取り出せることを確実に保証したい時には「collectionsモジュールのOrderedDictクラス」を使います。
from collections import OrderedDict
od = OrderedDict()
みたいに初期化して、あとは普通の辞書のように使えばよいです。
「od.items()」などで取り出した時に、キーの挿入順で取り出せます。
なんで、辞書の取り出し順がバージョンによって違うのかって話は、こちらの記事がすごいわかりやすかったので、リンク貼っておきます。
明示的にソートしてから一覧する① lambda 式を使う
ソートする場合は、一度辞書をソートしてから、上記の要領で一覧処理します。
キー・値でソートする例(昇順・降順)です。
from collections import defaultdict seed = ['orange','banana','apple','apple','apple','apple','orange','banana','banana','banana','banana','banana'] md = defaultdict(int) for key in seed: md[key] += 1 # キーの昇順にソートする smd1 = dict(sorted(md.items(),key=lambda x:x[0])) for key,val in zip(smd1.keys(),smd1.values()): print(key,":",val) # キーの降順にソートする smd2 = dict(sorted(md.items(),key=lambda x:x[0],reverse=True)) for key,val in zip(smd2.keys(),smd2.values()): print(key,":",val) # 値の昇順にソートする smd3 = dict(sorted(md.items(),key=lambda x:x[1])) for key,val in zip(smd3.keys(),smd3.values()): print(key,":",val) # 値の降順にソートする smd4 = dict(sorted(md.items(),key=lambda x:x[1],reverse=True)) for key,val in zip(smd4.keys(),smd4.values()): print(key,":",val)
この結果は上から順にこんな感じ。
apple : 4
banana : 6
orange : 2
orange : 2
banana : 6
apple : 4
orange : 2
apple : 4
banana : 6
banana : 6
apple : 4
orange : 2
ソートしてから一覧する②itemgetter()を使う
好みの問題ですが・・「 key=lambda x:x[1]」の部分が、いまいち好きではないと言う場合には、opratorモジュールのitemgetter()を使うやり方もあります。
一応、そちらも書いておきます。
from collections import defaultdict import operator as op seed = ['orange','banana','apple','apple','apple','apple','orange','banana','banana','banana','banana','banana'] md = defaultdict(int) for key in seed: md[key] += 1 # キーの昇順にソートする smd1 = dict(sorted(md.items(),key=op.itemgetter(0))) for key,val in zip(smd1.keys(),smd1.values()): print(key,":",val) # キーの降順にソートする smd2 = dict(sorted(md.items(),key=op.itemgetter(0),reverse=True)) for key,val in zip(smd2.keys(),smd2.values()): print(key,":",val) # 値の昇順にソートする smd3 = dict(sorted(md.items(),key=op.itemgetter(1))) for key,val in zip(smd3.keys(),smd3.values()): print(key,":",val) # 値の降順にソートする smd4 = dict(sorted(md.items(),key=op.itemgetter(1),reverse=True)) for key,val in zip(smd4.keys(),smd4.values()): print(key,":",val)
ちがうのは「key=op.itemgetter(1)」の部分だけです。
以上。
辞書の初期化・追加・一覧・ソートのポイントを整理してみました。
ではでは。