Ekstrak subset pasangan nilai kunci dari objek kamus Python?

313

Saya memiliki objek kamus besar yang memiliki beberapa pasangan nilai kunci (sekitar 16), tetapi saya hanya tertarik pada 3 di antaranya. Apa cara terbaik (terpendek / efisien / paling elegan) untuk mencapainya?

Yang terbaik yang saya tahu adalah:

bigdict = {'a':1,'b':2,....,'z':26} 
subdict = {'l':bigdict['l'], 'm':bigdict['m'], 'n':bigdict['n']}

Saya yakin ada cara yang lebih elegan dari ini. Ide ide?

Jayesh
sumber

Jawaban:

430

Kamu bisa mencoba:

dict((k, bigdict[k]) for k in ('l', 'm', 'n'))

... atau masuk Python 3Versi Python 2.7 atau yang lebih baru (terima kasih kepada Fábio Diniz karena menunjukkan bahwa itu berfungsi di 2.7 juga) :

{k: bigdict[k] for k in ('l', 'm', 'n')}

Pembaruan: Seperti yang ditunjukkan Håvard S , saya berasumsi bahwa Anda tahu kunci akan ada di kamus - lihat jawabannya jika Anda tidak dapat membuat asumsi itu. Atau, seperti yang ditunjukkan timbo dalam komentar, jika Anda ingin kunci yang hilang bigdictuntuk dipetakan None, Anda dapat melakukan:

{k: bigdict.get(k, None) for k in ('l', 'm', 'n')}

Jika Anda menggunakan Python 3, dan Anda hanya ingin kunci di dikt baru yang benar-benar ada di yang asli, Anda dapat menggunakan fakta untuk melihat objek mengimplementasikan beberapa operasi yang ditetapkan:

{k: bigdict[k] for k in bigdict.keys() & {'l', 'm', 'n'}}
Mark Longair
sumber
5
Akan gagal jika bigdicttidak mengandungk
Håvard S
7
Agak sulit untuk mengatakan itu - tampaknya cukup jelas dari konteksnya kepada saya bahwa diketahui bahwa kunci-kunci ini ada dalam kamus ...
Mark Longair
9
{k: bigdict.get(k,None) for k in ('l', 'm', 'n')}akan berurusan dengan situasi di mana kunci yang ditentukan tidak ada dalam kamus sumber dengan menetapkan kunci dict baru untuk Tidak ada
timbo
9
@MarkLongair Tergantung pada use case {k: bigdict [k] untuk k in ('l', 'm', 'n') jika k dalam bigdict} mungkin lebih baik, karena hanya menyimpan kunci yang benar-benar memiliki nilai.
Briford Wylie
6
bigdict.keys() & {'l', 'm', 'n'} ==> bigdict.viewkeys() & {'l', 'm', 'n'} untuk Python2.7
kxr
119

Sedikit lebih pendek, setidaknya:

wanted_keys = ['l', 'm', 'n'] # The keys you want
dict((k, bigdict[k]) for k in wanted_keys if k in bigdict)
Håvard S
sumber
8
+1 untuk perilaku alternatif mengecualikan kunci jika tidak ada dalam bigdict yang bertentangan dengan pengaturannya ke None.
dhj
1
Atau: dict((k,bigdict.get(k,defaultVal) for k in wanted_keys)jika Anda harus memiliki semua kunci.
Thomas Andrews
2
Jawaban ini disimpan oleh "t".
sakurashinken
24
interesting_keys = ('l', 'm', 'n')
subdict = {x: bigdict[x] for x in interesting_keys if x in bigdict}
ruang kepala
sumber
16

Sedikit perbandingan kecepatan untuk semua metode yang disebutkan:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Jan 29 2016, 14:26:21) [MSC v.1500 64 bit (AMD64)] on win32
In[2]: import numpy.random as nprnd
keys = nprnd.randint(1000, size=10000)
bigdict = dict([(_, nprnd.rand()) for _ in range(1000)])

%timeit {key:bigdict[key] for key in keys}
%timeit dict((key, bigdict[key]) for key in keys)
%timeit dict(map(lambda k: (k, bigdict[k]), keys))
%timeit dict(filter(lambda i:i[0] in keys, bigdict.items()))
%timeit {key:value for key, value in bigdict.items() if key in keys}
100 loops, best of 3: 3.09 ms per loop
100 loops, best of 3: 3.72 ms per loop
100 loops, best of 3: 6.63 ms per loop
10 loops, best of 3: 20.3 ms per loop
100 loops, best of 3: 20.6 ms per loop

Seperti yang diharapkan: pemahaman kamus adalah pilihan terbaik.

Sklavit
sumber
3 operasi pertama melakukan hal yang berbeda dengan dua yang terakhir, dan akan menghasilkan kesalahan jika keytidak ada bigdict.
apa
12

Jawaban ini menggunakan pemahaman kamus yang mirip dengan jawaban yang dipilih, tetapi tidak akan kecuali pada item yang hilang.

versi python 2:

{k:v for k, v in bigDict.iteritems() if k in ('l', 'm', 'n')}

versi python 3:

{k:v for k, v in bigDict.items() if k in ('l', 'm', 'n')}
meong
sumber
2
... tetapi jika dikt besar adalah BESAR itu masih akan diulangi sepenuhnya (ini adalah operasi O (n)), sedangkan invers hanya akan mengambil 3 item (masing-masing operasi O (1)).
Wouter Bolsterlee
1
Pertanyaannya adalah tentang kamus yang hanya terdiri dari 16 kunci
Meow
6

Mungkin:

subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n']])

Python 3 bahkan mendukung yang berikut ini:

subdict={a:bigdict[a] for a in ['l','m','n']}

Perhatikan bahwa Anda dapat memeriksa keberadaan dalam kamus sebagai berikut:

subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n'] if x in bigdict])

resp. untuk python 3

subdict={a:bigdict[a] for a in ['l','m','n'] if a in bigdict}
phimuemue
sumber
Gagal jika atidak dibigdict
Håvard S
3

Oke, ini adalah sesuatu yang telah menggangguku beberapa kali, jadi terima kasih Jayesh untuk menanyakannya.

Jawaban di atas sepertinya solusi yang baik, tetapi jika Anda menggunakan ini di seluruh kode Anda, masuk akal untuk membungkus fungsi IMHO. Juga, ada dua kemungkinan penggunaan di sini: satu di mana Anda peduli apakah semua kata kunci ada dalam kamus asli. dan satu di mana Anda tidak. Akan menyenangkan untuk memperlakukan keduanya dengan setara.

Jadi, untuk nilai dua sen saya, saya sarankan menulis sub-kelas kamus, mis

class my_dict(dict):
    def subdict(self, keywords, fragile=False):
        d = {}
        for k in keywords:
            try:
                d[k] = self[k]
            except KeyError:
                if fragile:
                    raise
        return d

Sekarang Anda bisa mengeluarkan sub-kamus dengan

orig_dict.subdict(keywords)

Contoh penggunaan:

#
## our keywords are letters of the alphabet
keywords = 'abcdefghijklmnopqrstuvwxyz'
#
## our dictionary maps letters to their index
d = my_dict([(k,i) for i,k in enumerate(keywords)])
print('Original dictionary:\n%r\n\n' % (d,))
#
## constructing a sub-dictionary with good keywords
oddkeywords = keywords[::2]
subd = d.subdict(oddkeywords)
print('Dictionary from odd numbered keys:\n%r\n\n' % (subd,))
#
## constructing a sub-dictionary with mixture of good and bad keywords
somebadkeywords = keywords[1::2] + 'A'
try:
    subd2 = d.subdict(somebadkeywords)
    print("We shouldn't see this message")
except KeyError:
    print("subd2 construction fails:")
    print("\toriginal dictionary doesn't contain some keys\n\n")
#
## Trying again with fragile set to false
try:
    subd3 = d.subdict(somebadkeywords, fragile=False)
    print('Dictionary constructed using some bad keys:\n%r\n\n' % (subd3,))
except KeyError:
    print("We shouldn't see this message")

Jika Anda menjalankan semua kode di atas, Anda akan melihat (seperti) output berikut (maaf untuk pemformatan):

Kamus asli:
{'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': 6, 'f': 5, 'i': 8, 'h': 7, 'k': 10, 'j': 9, 'm': 12, 'l': 11, 'o': 14, 'n': 13, 'q': 16, 'p': 15, 's': 18, 'r': 17, 'u': 20, 't': 19, 'w': 22, 'v': 21, 'y': 24, 'x ': 23,' z ': 25}

Kamus dari kunci bernomor ganjil:
{'a': 0, 'c': 2, 'e': 4, 'g': 6, 'i': 8, 'k': 10, 'm': 12, ' o ': 14,' q ': 16,' s ': 18,' u ': 20,' w ': 22,' y ': 24}

konstruksi subd2 gagal:
kamus asli tidak mengandung beberapa kunci

Kamus dikonstruksi menggunakan beberapa kunci buruk:
{'b': 1, 'd': 3, 'f': 5, 'h': 7, 'j': 9, 'l': 11, 'n': 13, 'p': 15, 'r': 17, 't': 19, 'v': 21, 'x': 23, 'z': 25}

Pandamonium
sumber
1
Subclassing membutuhkan objek dikt yang ada untuk dikonversi menjadi tipe subclass, yang bisa mahal. Mengapa tidak menulis fungsi sederhana saja subdict(orig_dict, keys, …)?
musiphil
3

Anda juga dapat menggunakan map(yang merupakan fungsi yang sangat berguna untuk mengenal):

sd = dict(map(lambda k: (k, l.get(k, None)), l))

Contoh:

large_dictionary = {'a1':123, 'a2':45, 'a3':344}
list_of_keys = ['a1', 'a3']
small_dictionary = dict(map(lambda key: (key, large_dictionary.get(key, None)), list_of_keys))

PS: Saya meminjam .get(key, None)dari jawaban sebelumnya :)

halfdanrump
sumber
1

Satu lagi (saya lebih suka jawaban Mark Longair)

di = {'a':1,'b':2,'c':3}
req = ['a','c','w']
dict([i for i in di.iteritems() if i[0] in di and i[0] in req])
georg
sumber
yang lambat untuk besar dict ini
kxr
0

larutan

from operator import itemgetter
from typing import List, Dict, Union


def subdict(d: Union[Dict, List], columns: List[str]) -> Union[Dict, List[Dict]]:
    """Return a dict or list of dicts with subset of 
    columns from the d argument.
    """
    getter = itemgetter(*columns)

    if isinstance(d, list):
        result = []
        for subset in map(getter, d):
            record = dict(zip(columns, subset))
            result.append(record)
        return result
    elif isinstance(d, dict):
        return dict(zip(columns, getter(d)))

    raise ValueError('Unsupported type for `d`')

contoh penggunaan

# pure dict

d = dict(a=1, b=2, c=3)
print(subdict(d, ['a', 'c']))

>>> In [5]: {'a': 1, 'c': 3}
# list of dicts

d = [
    dict(a=1, b=2, c=3),
    dict(a=2, b=4, c=6),
    dict(a=4, b=8, c=12),
]

print(subdict(d, ['a', 'c']))

>>> In [5]: [{'a': 1, 'c': 3}, {'a': 2, 'c': 6}, {'a': 4, 'c': 12}]
DmitrySemenov
sumber