Estrarre un sottoinsieme di coppie chiave-valore dall'oggetto dizionario Python?


313

Ho un grande oggetto dizionario che ha diverse coppie chiave-valore (circa 16), ma sono interessato solo a 3 di esse. Qual è il modo migliore (più breve / efficiente / più elegante) per raggiungere questo obiettivo?

Il meglio che conosco è:

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

Sono sicuro che esiste un modo più elegante di questo. Idee?

Risposte:


430

Puoi provare:

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

... o dentro Python 3Python versioni 2.7 o successive (grazie a Fábio Diniz per averlo sottolineato che funziona anche in 2.7) :

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

Aggiornamento: come sottolinea Håvard S , presumo che tu sappia che le chiavi saranno nel dizionario - vedi la sua risposta se non sei in grado di fare quell'ipotesi. In alternativa, come sottolinea Timbo nei commenti, se si desidera una chiave mancante bigdictper mappare None, è possibile fare:

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

Se stai usando Python 3 e vuoi solo le chiavi nel nuovo dict effettivamente presenti in quello originale, puoi usare il fatto per visualizzare gli oggetti implementando alcune operazioni set:

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

5
Fallirà se bigdictnon contienek
Håvard S

7
Un po 'duro da sottovalutare - mi è sembrato abbastanza chiaro dal contesto che è noto che queste chiavi sono nel dizionario ...
Mark Longair

9
{k: bigdict.get(k,None) for k in ('l', 'm', 'n')}affronterà la situazione in cui manca una chiave specificata nel dizionario di origine impostando la chiave nel nuovo dict su None
timbo

9
@MarkLongair A seconda del caso d'uso {k: bigdict [k] per k in ('l', 'm', 'n') se k in bigdict} potrebbe essere migliore, in quanto memorizza solo le chiavi che hanno effettivamente dei valori.
Briford Wylie

6
bigdict.keys() & {'l', 'm', 'n'} ==> bigdict.viewkeys() & {'l', 'm', 'n'} per Python2.7
kxr

119

Un po 'più breve, almeno:

wanted_keys = ['l', 'm', 'n'] # The keys you want
dict((k, bigdict[k]) for k in wanted_keys if k in bigdict)

8
+1 per il comportamento alternativo di esclusione di una chiave se non è in errore invece di impostarla su Nessuno.
dhj

1
In alternativa: dict((k,bigdict.get(k,defaultVal) for k in wanted_keys)se devi avere tutte le chiavi.
Thomas Andrews,

2
Questa risposta è salvata da una "t".
sakurashinken,

24
interesting_keys = ('l', 'm', 'n')
subdict = {x: bigdict[x] for x in interesting_keys if x in bigdict}

16

Un po 'di confronto della velocità per tutti i metodi citati:

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

Come previsto: la comprensione del dizionario è l'opzione migliore.


Le prime 3 operazioni stanno facendo una cosa diversa dalle ultime due e si tradurrà in un errore se keynon esiste bigdict.
naught101

12

Questa risposta usa una comprensione del dizionario simile alla risposta selezionata, ma non lo farà se non su un elemento mancante.

versione di Python 2:

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

versione di Python 3:

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

2
... ma se il big dict è ENORME verrà comunque ripetuto completamente (questa è un'operazione O (n)), mentre l'inverso prenderebbe solo 3 elementi (ciascuno un'operazione O (1)).
Wouter Bolsterlee,

1
La domanda riguarda un dizionario di sole 16 chiavi
Meow,

6

Può essere:

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

Python 3 supporta anche quanto segue:

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

Si noti che è possibile verificare l'esistenza nel dizionario come segue:

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

resp. per Python 3

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

Non funziona se anon è presentebigdict
Håvard S

3

Ok, questo è qualcosa che mi ha infastidito alcune volte, quindi grazie Jayesh per avermelo chiesto.

Le risposte sopra sembrano un'ottima soluzione come qualsiasi, ma se lo stai usando in tutto il tuo codice, ha senso avvolgere la funzionalità IMHO. Inoltre, ci sono due possibili casi d'uso qui: uno in cui ti preoccupi se tutte le parole chiave sono nel dizionario originale. e uno dove non lo fai. Sarebbe bello trattare entrambi allo stesso modo.

Quindi, per il mio valore di due penneth, suggerisco di scrivere una sottoclasse di dizionario, ad es

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

Ora puoi estrarre un sotto-dizionario con

orig_dict.subdict(keywords)

Esempi di utilizzo:

#
## 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")

Se esegui tutto il codice sopra, dovresti vedere (qualcosa di simile) il seguente output (scusa per la formattazione):

Dizionario originale:
{'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}

Dizionario da tasti dispari:
{'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}

costruzione subd2 fallita: il
dizionario originale non contiene alcune chiavi

Dizionario creato utilizzando alcuni tasti errati:
{'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}


1
La sottoclasse richiede la conversione di un oggetto dict esistente nel tipo di sottoclasse, che può essere costoso. Perché non scrivere semplicemente una funzione subdict(orig_dict, keys, …)?
musiphil,

3

Puoi anche usare map(che è una funzione molto utile per conoscere comunque):

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

Esempio:

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: l'ho preso in prestito .get(key, None)da una risposta precedente :)


1

Ancora un altro (preferisco la risposta di 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])

la sua lenta per il grande dict di
KXR

0

soluzione

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`')

esempi di utilizzo

# 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}]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.