"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Python3辞書型の初期化・追加・一覧・ソートの方法と注意点

f:id:arakan_no_boku:20181124131221j:plain

目次

Pythonの辞書(dictionary)

Pythonの辞書(dictionary)は他の言語で "マップ"とか  "連想配列" と呼ばれているものと同じように使えます。

辞書は 文字列、数値および。文字列、数値、その他のタプルのみを含むタプルをキーにできます。

今回は、辞書のの基本操作(初期化・追加・一覧・ソート)をまとめます。

基本操作1:辞書型の初期化 

空で初期化するだけなら簡単です。

dic={}

一次元で静的に値を与えて初期化するなら。

dic={ 'A': 'VVV1', 'B', 'VVV2' } 

 のように、「’KEY': 'VALUE'」の組合せで初期値を与えます。

ネストする場合は少々長くなるので、別記事にしています。

arakan-pgm-ai.hatenablog.com 

基本操作2:データ追加

キーとする値が存在する場合は簡単です。

辞書の名前を、仮にmdとすると・

dic[key]=value

としたり

md[key] += 1  

みたいに、カウントアップしたりするだけです。

ただ、一般的な辞書(dictianary)は、存在しないキーを指定するとエラーになります。 

その問題回避の方法としては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とかで連想配列を覚えた人間にとっては、キーがない時にエラーにせず、自動的にキーを追加してほしいです。 

それをかなえてくれる辞書もPythonにはあります。

デフォルト辞書(defaultdict)です。

違いは初期化の仕方だけです。 

① collectionsモジュールのdefaultdictクラスをインポートする。

② defaultdict()で初期化する。

これだけで、以下のようにスマートにカウントできるようになります。

from collections import defaultdict

seed = ['apple','orange','banana','orange','banana','apple','apple','apple']

md = defaultdict(int)
for key in seed:
    md[key] += 1

 

defaultdictクラスの引数のintは、デフォルト値「0」を返す関数です。

変数と勘違いしやすいので、ご注意ください。。 

デフォルト辞書は、普通の辞書と同じように使えます。

ただ、djangoテンプレートを使うときにはデフォルト辞書を直接渡せない場合があるなど、一部、制限事項があるので、そこを認識して使う必要があります。

arakan-pgm-ai.hatenablog.com 

基本操作3:辞書にセットしたキーや値を参照 

個別にキーを指定して取得する方法と、forループをまわすために一括で取得するか方法をまとめておきます。 

参照方法(1):個別にキーを指定して取得 

配列の引数みたいに個別のキーを指定して値を取得するやり方です。

例えば。

info = {'apple':'りんごだよ','orange':'みかんだよ','banana':'ばななだよ'}

みたいな辞書を初期化で展開しておいたなら。

info['apple']

で「りんごだよ」の文字列を得るみたいな感じです。

文字列をキーにする「配列」というイメージ通りでいけます。 

参照方法(2):全てのキーと値を一覧 (items)

キーの一覧(リスト)は、「keys()」で取得できます。

値の一覧(リスト)は、「values()」で取得できます。

キーと値をセットで取得するには「items()」で取得できます。

for key,val in md.items():
    print(key,":",val)

みたいな感じですね。

参照方法(3):すべてのキーと値を一覧(zipを使う)

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()」を使うのが無難です。 

辞書をソートして一覧(1):ソートできる辞書を使う 

辞書から値を取り出して一覧したりするときのソート(並び順)に関するまとめです。

辞書のソート順はバージョンに依存します。

Python3.7からは言語仕様で、標準の辞書でも挿入順が保証され、Python3.6でもCPythonの実装ベースで同様の動きをします。

しかし、それ以外だと保証されません。

なので。

バージョンにかかわらず、キーの挿入順を記録して、その順番で取り出せることを確実に保証したい時には「collectionsモジュールのOrderedDictクラス」を使います。

from collections import OrderedDict

od = OrderedDict()

 みたいに初期化して、あとは普通の辞書のように使えばよいです。

「od.items()」などで取り出した時に、キーの挿入順で取り出せます。

なんで、辞書の取り出し順がバージョンによって違うのかって話は、こちらの記事がすごいわかりやすかったので、リンク貼っておきます。

www.freia.jp 

しかし、それ以外の辞書で取り出して、任意の並び順で一覧するには、以下のような方法を使う必要があります。

辞書をソートして一覧(2):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 

辞書をソートして一覧(3):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)」の部分だけです。

以上、辞書の初期化・追加・一覧・ソートのポイントを整理してみました。

ではでは。