特定のキーのみを含むようにdictをフィルタリングしますか?


496

dictたくさんのエントリがあるを持っています。そのうちのいくつかだけに興味があります。他のすべてのものを削除する簡単な方法はありますか?


どのタイプのキー(整数?文字列?日付?任意のオブジェクト?)を言い、どのキーが入っているか出ていないかを確認する単純な(文字列、正規表現、リストメンバーシップ、または数値的不等式)テストがあるかどうかを言うと役立ちます。それ以外の場合は、それを決定するために任意の関数を呼び出す必要がありますか?
smci

@smci文字列キー。私が他に何でも使えるとは思いもしませんでした。私は長い間JSとPHPでコーディングしてきました...
mpen

回答:


656

新しい辞書を作成する:

dict_you_want = { your_key: old_dict[your_key] for your_key in your_keys }

辞書内包表記を使用します。

それらがないバージョン(つまり、Python 2.6以前)を使用する場合は、それを作成してくださいdict((your_key, old_dict[your_key]) for ...)。醜いですが、同じです。

これは、jnnnnnのバージョンとは異なりold_dict、任意のサイズのs に対して安定したパフォーマンス(your_keysの数にのみ依存)があることに注意してください。速度とメモリの両方の面で。これはジェネレータ式であるため、一度に1つの項目を処理し、old_dictのすべての項目を調べません。

すべてをインプレースで削除:

unwanted = set(keys) - set(your_dict)
for unwanted_key in unwanted: del your_dict[unwanted_key]

8
「辞書が含まれていないバージョンを使用している場合、辞書内包表記を使用します」==バージョン<= 2.6
getekha

8
ファイラーキーの1つがold_dictに存在しない場合、KeyErrorをスローします。私は{k:d [k] for filter in k if d if k in d}
Peter Gibson

1
@PeterGibsonはい、それが要件の一部である場合、あなたはそれについて何かをする必要があります。暗黙のうちにキーを削除したり、デフォルト値を追加したりするかどうかは、何をしているのかに依存します。あなたのアプローチが間違っているユースケースはたくさんあります。キーの欠落がold_dict他の場所にあるバグを示している場所も多くあります。その場合、私は黙って間違った結果よりもエラーを優先します。

@delnan、「if k in d」を追加すると、dが大きい場合に速度が低下します。言及する価値があると思いました
Peter Gibson

7
@PeterGibsonありません。辞書の検索はO(1)です。

130

少しエレガントな辞書の理解:

foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}

賛成。これに似た答えを追加することを考えていました。しかし、好奇心から抜け出して、なぜ{k:dict [k] for k in dict ...}ではなく{k:v for k、v in dict.items()...}を行うのですか?パフォーマンスに違いはありますか?
Hart Simha 2014年

4
私自身の質問に答えました。{k:dict [k] for k in dict ...}は、少なくともPython 2.7.6では、26項目の辞書(timeit(...、setup = "d = {chr(x + 97):x + 1 for x in range(26)} "))、フィルターで除外される項目の数に応じて(子音キーのフィルターは、検索しているため、母音キーのフィルターよりも高速です)少ないアイテム)。辞書のサイズが大きくなるにつれて、パフォーマンスの違いはそれほど重要ではなくなります。
Hart Simha 2014年

5
mydict.iteritems()代わりに使用した場合、おそらく同じパフォーマンスになります。.items()別のリストを作成します。
Pat

64

Python 2.6の例を次に示します。

>>> a = {1:1, 2:2, 3:3}
>>> dict((key,value) for key, value in a.iteritems() if key == 1)
{1: 1}

フィルター部分はifステートメントです。

この方法は、非常に多くのキーのいくつかだけを選択したい場合、delnanの回答よりも遅くなります。


11
たぶん私が使用するif key in ('x','y','z')と思いますが、
mpen

必要なキーがすでにわかっている場合は、delnanの答えを使用してください。ifステートメントで各キーをテストする必要がある場合は、ランズフォードの回答を使用してください。
jnnnnn 2015

1
このソリューションにはもう1つの利点があります。高価な関数呼び出しから辞書が返された場合(つまり、a / old_dictは関数呼び出しです)、このソリューションは関数を1回だけ呼び出します。関数によって返されたディクショナリを変数に格納する命令型環境では大したことではありませんが、関数環境(ラムダなど)では、これが重要な観察結果です。
gae123 2016年


20

コード1:

dict = { key: key * 10 for key in range(0, 100) }
d1 = {}
for key, value in dict.items():
    if key % 2 == 0:
        d1[key] = value

コード2:

dict = { key: key * 10 for key in range(0, 100) }
d2 = {key: value for key, value in dict.items() if key % 2 == 0}

コード3:

dict = { key: key * 10 for key in range(0, 100) }
d3 = { key: dict[key] for key in dict.keys() if key % 2 == 0}

すべてのピースのコードパフォーマンスは、timeitでnumber = 1000を使用して測定され、コードの各ピースについて1000回収集されます。

ここに画像の説明を入力してください

Python 3.6の場合、3つの方法のdict辞書キーのパフォーマンスはほぼ同じです。Python 2.7の場合、コード3はわずかに高速です。


好奇心旺盛ですが、Pythonからそのプロットを作成しましたか?
user5359531 2017年


18

この1つのライナーラムダは機能するはずです。

dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])

次に例を示します。

my_dict = {"a":1,"b":2,"c":3,"d":4}
wanted_keys = ("c","d")

# run it
In [10]: dictfilt(my_dict, wanted_keys)
Out[10]: {'c': 3, 'd': 4}

これは、dictキー(i in x)を反復処理する基本的なリスト理解であり、キーが目的のキーリスト(y)にある場合、タプル(key、value)ペアのリストを出力します。dict()は全体をラップしてdictオブジェクトとして出力します。


setforを使用する必要がありますwanted_keysが、それ以外は問題ありません。
mpen 2013年

これにより、元の辞書に値の代わりにリストが含まれている場合、空白の辞書が表示されます。回避策はありますか?
FaCoffee、2015年

@フランチェスコ、例を提供できますか?私が実行すると:dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z')){'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}意図したとおりに戻ります。
ジム

私はこれを試してみました:dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}結果はでした{}。これは空白の辞書であると想定していました。
FaCoffee 2015年

1つは、「dict」は予約語であるため、dictの名前に使用しないでください。あなたが引き出そうとしていた鍵は何でしたか?私が実行すると:foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2'))、次のようになります。{'0': [1, 3], '2': [1, 4]}これは意図された結果です
ジム

14

あなたのオリジナルの辞書を考えるorigと、あなたが興味を持っていることをエントリのセットをkeys

filtered = dict(zip(keys, [orig[k] for k in keys]))

これはデルナンの答えほど良くはありませんが、対象となるすべてのPythonバージョンで機能するはずです。ただし、keys元の辞書に存在する各要素に対して脆弱です。


さて、これは基本的に私のdictの理解の「タプルジェネレーターバージョン」の熱心なバージョンです。実際、非常に互換性がありますが、ジェネレータ式は2005年春の2.4で導入されましたが、真剣に、まだこれを使用している人はいますか?

1
私は反対しません。2.3はもう存在すべきではありません。ただし、2.3の使用に関する古い調査として:moinmo.in/PollAboutRequiringPython24ショートバージョン:RHEL4、SLES9、OS X 10.4に同梱
Kai

7

デルナンが認めた回答に基づく。

必要なキーの1つがold_dictにない場合はどうなりますか?delnanソリューションは、キャッチできるKeyError例外をスローします。それが必要なものでない場合は、次のようにしたいでしょう:

  1. old_dictとwantd_keysのセットの両方で興奮するキーのみを含めます。

    old_dict = {'name':"Foobar", 'baz':42}
    wanted_keys = ['name', 'age']
    new_dict = {k: old_dict[k] for k in set(wanted_keys) & set(old_dict.keys())}
    
    >>> new_dict
    {'name': 'Foobar'}
  2. old_dictで設定されていないキーのデフォルト値があります。

    default = None
    new_dict = {k: old_dict[k] if k in old_dict else default for k in wanted_keys}
    
    >>> new_dict
    {'age': None, 'name': 'Foobar'}

あなたも行うことができます{k: old_dict.get(k, default) for k in ...}
Moberg

6

この関数はトリックを行います:

def include_keys(dictionary, keys):
    """Filters a dict by only including certain keys."""
    key_set = set(keys) & set(dictionary.keys())
    return {key: dictionary[key] for key in key_set}

delnanのバージョンと同じように、これは辞書内包表記を使用し、大きな辞書に対して安定したパフォーマンスを発揮します(辞書内のキーの総数ではなく、許可するキーの数にのみ依存します)。

MyGGanのバージョンと同じように、このリストでは、キーのリストに辞書に存在しない可能性のあるキーを含めることができます。

おまけとして、これは逆です。元の特定のキーを除外することで辞書を作成できます。

def exclude_keys(dictionary, keys):
    """Filters a dict by excluding certain keys."""
    key_set = set(dictionary.keys()) - set(keys)
    return {key: dictionary[key] for key in key_set}

delnanのバージョンとは異なり、操作は適切に行われないため、パフォーマンスは辞書内のキーの数に関連していることに注意してください。ただし、これの利点は、関数が提供された辞書を変更しないことです。

編集:辞書から特定のキーを除外する別の機能を追加しました。


セットが受け入れるkeysものなど、あらゆる種類のイテラブルで許可する必要があります。
mpen 2013

ああ、いい電話、これを指摘してくれてありがとう。その更新を行います。
ライアン

2つの機能があれば元気になるかな。10人に「議論が維持されるinvertことを意味するのkeysか、それともkeys拒否されるのか」と尋ねた場合、何人が同意するでしょうか。
スケート選手、2015

更新しました。どう考えているか教えてください。
ライアン

入力辞書に値の代わりにリストがある場合、これは機能していないようです。この場合、無効な口述を取得します。回避策はありますか?
FaCoffee、2015年

4

選択したキーを削除して新しいディクショナリを作成する場合は、ディクショナリ内包を利用できます
。次に例を示します。

d = {
'a' : 1,
'b' : 2,
'c' : 3
}
x = {key:d[key] for key in d.keys() - {'c', 'e'}} # Python 3
y = {key:d[key] for key in set(d.keys()) - {'c', 'e'}} # Python 2.*
# x is {'a': 1, 'b': 2}
# y is {'a': 1, 'b': 2}

きちんと。Python 3でのみ機能します
。Python2

Python 2のset(d.keys())が追加されました。これは実行すると機能します。
Srivastava

2

別のオプション:

content = dict(k1='foo', k2='nope', k3='bar')
selection = ['k1', 'k3']
filtered = filter(lambda i: i[0] in selection, content.items())

しかし、ではなくからlist返される(Python 2)またはイテレータ(Python 3)を取得します。 filter()dict


包み込んfiltereddict、辞書を取り戻しましょう!
CMCDragonkai

1

ショートフォーム:

[s.pop(k) for k in list(s.keys()) if k not in keep]

ほとんどの回答が簡潔さを維持するために示唆しているように、それがaでlistあれであれ、複製オブジェクトを作成する必要がありdictます。これは使い捨てを作成listしますが、元ののキーを削除しますdict


0

del1つのライナーで使用する別の簡単な方法を次に示します。

for key in e_keys: del your_dict[key]

e_keys除外するキーのリストです。新しい辞書を与えるのではなく、辞書を更新します。

新しい出力辞書が必要な場合は、削除する前に辞書のコピーを作成します。

new_dict = your_dict.copy()           #Making copy of dict

for key in e_keys: del new_dict[key]

0

あなたはを使うことができますpython-benedict、それはdictサブクラスです。

インストール: pip install python-benedict

from benedict import benedict

dict_you_want = benedict(your_dict).subset(keys=['firstname', 'lastname', 'email'])

GitHubのオープンソースです:https : //github.com/fabiocaccamo/python-benedict


免責事項:私はこのライブラリの作者です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.