スポンサーリンク

Pythonの辞書を使いこなす!defaultdictの活用方法

どうも、きっしゅです。

Pythonでのプログラムの作成がある程度慣れてくると、高度な実装にも手を出したくなると思います。

本記事ではPythonの辞書の高度な活用方法、defaultdictの使い方、活用方法を紹介します。

辞書型のデータを扱う時の1番のリスクは「キーが存在しないこと」だと思います。キーが存在しないとエラーが発生し、エラーハンドリングをしっかりとしていないとそこで処理が強制終了されてしまいます。

今回紹介するdefaultdict()関数はキーが存在しないことによるエラーを防ぐのはもちろん、キーがない場合は新しい要素を追加してくれます。
そのためキーの存在有無を気にせず実装をすることができ非常に便利です。

defaultdict()関数を使いこなすことができると一気に実装の幅が広がり、今まで複雑に実装していたコードが非常にシンプルなコードになると思います。

是非参考にしてください!

※本記事はPythonの辞書型に関する基本的な理解がある前提です。そもそも辞書型ってなに?って方は下記の記事を参考に基礎の部分の理解を優先することをお勧めしします。

スポンサードリンク

Python 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイルを学ぶオンライン講座

defaultdictの活用方法

defaultdictの使い方と活用方法

まずはdefaultdict()関数の紹介です。

defaultdictはsetdefaultの上位互換のような関数です。
※完全な上位互換ではありません。

どのようなキーがきても指定したデフォルト値を初期値として要素を追加します。
そのためsetdefaultみたいにキーごとにデフォルト値を設定する必要がありません。

では早速使い方を紹介します。

defaultdictの使い方

まずdefaultdictは標準ライブラリなので別途importする必要があります。

import文は下記の通りです。
一旦今回はdefaultdictしか使わないのでdefaultdictのみimportしておきます。

from collections import defaultdict

そして作成時には下記のように書きます。
引数には関数を渡す必要があります。

default_dict = defaultdict(関数名)

ではこれがどのように動くのかサンプルコードを使って確認します。

from collections import defaultdict


def default_value():
    return 'Tokyo'


default_dict = defaultdict(default_value)
print(f'Initial value -> {default_dict}')
# Initial value -> defaultdict(<function default_value at 0x103c50e50>, {})


# 値を追加する
default_dict['Name'] = 'Taro'
default_dict['Age'] = 20
print(f'After add data -> {default_dict}')
# After add data -> defaultdict(<function default_value at 0x103c50e50>, {'Name': 'Taro', 'Age': 20})


# キーが存在する値を取得する
name = default_dict['Name']
print(f'I am {name}!')
# I am Taro!
name_2 = default_dict.get('Name')
print(f'I am {name_2}!')
# I am Taro!


# キーの存在しない値を取得する
from_ = default_dict['From']
print(f'I am from {from_}')
# I am from Tokyo

# getを使って取得するとdeafultdictは機能しないため None が取得される
weight = default_dict.get('Weight')
print(f'I am from {weight}')
# I am from None


print(f'Show dict values -> {default_dict}')
# Show dict values -> defaultdict(<function default_value at 0x103c089d0>, {'Name': 'Taro', 'Age': 20, 'From': 'Tokyo'})

今回は引数として渡すための関数としてdefault_valueという関数を用意しました。この関数のReturn値がキーが存在しなかった時にセットされる要素の値になります。

まず8行目でdefaultdictを作成して引数にdefault_value関数を設定しています。

次に14、15行目で要素を追加しています。今回はNameAgeを追加しました。
要素の追加の方法は通常の辞書の場合と同じです。Keyを指定して値を追加していきます。

次に21行目では追加した要素に対してキーを指定して値を取得しています。
ここも値の追加同様、通常の辞書と同じです。キーを指定してアクセスすることとも可能ですし、get()関数を使ってアクセスすることも可能です。

続いては30行目で存在しないキーにアクセスを試みています。
もちろんここではエラーは発生しません。また返り値は出力結果をみてわかるようにdefault_value関数の返り値になっています。

ちなみに35行目のようにget()関数を使うとNoneが返ってきます。つまり通常のキー指定のアクセスの場合のみデフォルトで設定した関数の返り値が返ってきます。

この動きよりdefault()関数の動きは下記のようにまとめることができます。

  • 通常の辞書のようにキーを指定して値を追加することが可能
  • 通常の辞書のようにキーを指定して値を取得することが可能
  • 存在しないキーを指定して値を取得しようとすると、設定した関数の初期値が返ってくる
  • get()関数を使った存在しないキーにアクセスした場合は設定した関数の初期値は返ってこず、get()関数で設定した値が返ってくる

defaultdictの引数に渡す関数に関して

まずはdefaultdictでよく引数に渡す関数とその初期値を紹介します。
これらは頻繁に使うと思うので覚えおくことをお勧めします。

関数名初期値
int0
list[]
dict{}
boolFalse

実際に動かして確認してみると下記のようになります。

from collections import defaultdict


# int
default_dict_int = defaultdict(int)
val_int = default_dict_int['hoge']
print(f'Dictionary values -> {default_dict_int}')
print(f'Return value      -> {val_int}')
# Dictionary values -> defaultdict(<class 'int'>, {'hoge': 0})
# Return value      -> 0


# list
default_dict_list = defaultdict(list)
val_list = default_dict_list['hoge']
print(f'Dictionary values -> {default_dict_list}')
print(f'Return value      -> {val_list}')
# Dictionary values -> defaultdict(<class 'list'>, {'hoge': []})
# Return value      -> []


# dict
default_dict_dict = defaultdict(dict)
val_dict = default_dict_dict['hoge']
print(f'Dictionary values -> {default_dict_dict}')
print(f'Return value      -> {val_dict}')
# Dictionary values -> defaultdict(<class 'dict'>, {'hoge': {}})
# Return value      -> {}


# bool
defaultdict_bool = defaultdict(bool)
val_bool = defaultdict_bool['hoge']
# Dictionary values -> defaultdict(<class 'bool'>, {'hoge': False})
# Return value      -> False

続いてはLambda関数です。Lambda関数を使うとわざわざ別で関数を定義する必要がなくなるのでコード量が減ります。

Lambda関数を使う場合は下記のように書きます。

from collections import defaultdict


default_dict = defaultdict(lambda: 'Tokyo')
print(f'Initial value -> {default_dict}')


# Access no existing value
from_ = default_dict['From']
print(f'I am from {from_}')
print(f'Show dict values -> {default_dict}')
# I am from Tokyo
# defaultdict(<function <lambda> at 0x10e3aea60>, {'From': 'Tokyo'})

こうすると非常にシンプルに書くことができます。

続いては、このdefaultdictが実際のプログラムでどのように活用されるのかを紹介します。

defaultdictの活用方法

defaultdictが力を発揮するのはいろいろな場面がありますが、カウントする時に活用している場面をよく見かけます。

まずは一番シンプルな実装で文字数をカウントしてみます。
処理内容はdefaultdictという文字列に同じ文字が何個含まれているかをカウントしています。

# defaultdict未使用
str_ = 'defaultdict'
normal_dict = {}
for j in str_:
    if j in normal_dict:
    # if normal_dict.get(j): でも可能
        normal_dict[j] += 1
    else:
        normal_dict[j] = 1

print(f'Count words -> {normal_dict}')
# Count words -> {'d': 2, 'e': 1, 'f': 1, 'a': 1, 'u': 1, 'l': 1, 't': 2, 'i': 1, 'c': 1}

通常ですとキーの存在をif文でチェックし、存在しない場合は初期値を設定する処理を書く必要があります。

またPythonの場合inを使った処理は重たいので大きな辞書を扱う時のパフォーマンスに関する懸念点も少し残ります。

これをdefaultdictを使って実装してみると下記のようになります。

# defaultdictを使った場合
from collections import defaultdict

str_ = 'defaultdict'

defaultdict_ = defaultdict(int)

for i in str_:
    defaultdict_[i] += 1

print(f'Count words -> {defaultdict_}')
# Count words -> defaultdict(<class 'int'>, {'d': 2, 'e': 1, 'f': 1, 'a': 1, 'u': 1, 'l': 1, 't': 2, 'i': 1, 'c': 1})

キーの存在チェックや初期値の設定が必要ないのでたった1行でシンプルに実装することができます。
これをみるともうdefaultdictを使わない理由がないと思います。

少し余談ですが、このような1次元のカウントの場合はCounter()関数を使って実装することも可能です。実際にコードを書いてみると以下のようになります。

from collections import Counter, defaultdict

str_ = 'defaultdict'

count = Counter(str_)
print(f'Count words -> {count}')
# Counter({'d': 2, 't': 2, 'e': 1, 'f': 1, 'a': 1, 'u': 1, 'l': 1, 'i': 1, 'c': 1})

シンプルなカウントであればCounter()関数を使った方がより簡単に実装が可能です。
Counterの詳細に関しては下記の記事で紹介しています。

そうなるとdefaultdictが活躍するのはどのような場面かというと、以下のような多次元の辞書を作成する時に活躍します。

from collections import defaultdict


# 食べ物の好き嫌いを3段階評価してもらった結果
questionnaire_results = {
    'sushi': {
        'Taro': 3,
        'Jiro': 3,
        'Hanako': 3,
        'Pochi': 3,
        'Tama': 2,
    },
    'stake': {
        'Taro': 3,
        'Jiro': 2,
        'Hanako': 1,
        'Pochi': 2,
        'Tama': 2,
    },
    'ramen': {
        'Taro': 1,
        'Jiro': 1,
        'Hanako': 1,
        'Pochi': 3,
        'Tama': 2,
    },
    'curry': {
        'Taro': 2,
        'Jiro': 2,
        'Hanako': 3,
        'Pochi': 1,
        'Tama': 2,
    },
}

# それぞれの食べ物のどの数字に何票入っているかをカウント
counted_res = defaultdict(lambda: defaultdict(int))

for food, evaluation_dict in questionnaire_results.items():
    for voter, num in evaluation_dict.items():
        counted_res[food][num] += 1

print(f'Counted result -> {counted_res}')
# counted_res: {
#        curry: {
#            1: 1,
#            2: 3,
#            3: 1,
#        },
#         ramen: {
#            1: 3,
#            2: 1,
#            3: 1,
#        },
#        stake: {
#            1: 1,
#            2: 3,
#            3: 1,
#        },
#        sushi: {
#            2: 1,
#            3: 4,
#        },
#    },

上記の処理はそれぞれの食べ物に対して好き嫌いを3段階評価で評価してもらい、どの評価に何票入っているかを集計している処理です。

このよう多次元の辞書を作成するような処理であってもdefaultdictを使えば非常にシンプルな実装が可能です。

アンケート集計やデータ分析の際にはこのように多次元のデータを扱うことが頻繁にあると思います。defaultdictをマスターしているとシンプルでコード量が少ない実装が可能となるので、マスターして損することは絶対にないと思います。

defaultdictはネストの深い辞書を作成する際により力を発揮する

まとめ

いかがでしたでしょうか?

今回はdefaultdict()関数に関して紹介しました。
defaultdict()関数シンプルな処理であれば他の方法でも十分代用できます。ですが、ネストが深い辞書を扱うようになればなるほどパワーを発揮します。

データ分析をはじめとしてあらゆる場面で活用できるのでぜひマスターしてみてください!

みなさんの最高のPythonライフの役に立ちますと幸いです。

Python
スポンサーリンク
ibukishをフォローする
スポンサーリンク
ibukish Lab+

コメント

  1. […] 【Pythonの辞書を使いこなせ!】defaultdictの活用方法みなさんはPythonの辞書(ディクショナリー)を使いこなしていますか?辞書は非常に多くの可能性を秘めています。本記事ではそんな […]

タイトルとURLをコピーしました