Extraire un sous-ensemble de paires clé-valeur de l'objet dictionnaire Python?


313

J'ai un gros objet dictionnaire qui a plusieurs paires de valeurs clés (environ 16), mais je ne suis intéressé que par 3 d'entre elles. Quelle est la meilleure façon (la plus courte / efficace / la plus élégante) pour y parvenir?

Le mieux que je sache est:

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

Je suis sûr qu'il existe une manière plus élégante que cela. Des idées?

Réponses:


430

Tu pourrais essayer:

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

... ou dans Python 3Python versions 2.7 ou ultérieures (merci à Fábio Diniz d' avoir souligné que cela fonctionne aussi en 2.7) :

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

Mise à jour: Comme le souligne Håvard S , je suppose que vous savez que les clés seront dans le dictionnaire - voir sa réponse si vous n'êtes pas en mesure de faire cette hypothèse. Alternativement, comme le souligne timbo dans les commentaires, si vous voulez une clé manquante bigdictà mapper None, vous pouvez faire:

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

Si vous utilisez Python 3, et que vous voulez uniquement des clés dans le nouveau dict qui existent réellement dans l'original, vous pouvez utiliser le fait pour afficher les objets implémenter certaines opérations d'ensemble:

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

5
Échouera si bigdictne contient pask
Håvard S

7
Un peu dur pour déprécier cela - il m'a semblé assez clair du contexte que l'on sait que ces clés sont dans le dictionnaire ...
Mark Longair

9
{k: bigdict.get(k,None) for k in ('l', 'm', 'n')}traitera la situation où une clé spécifiée est manquante dans le dictionnaire source en définissant la clé du nouveau dict sur None
timbo

9
@MarkLongair Selon le cas d'utilisation {k: bigdict [k] pour k dans ('l', 'm', 'n') si k dans bigdict} pourrait être mieux, car il ne stocke que les clés qui ont réellement des valeurs.
Briford Wylie

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

119

Un peu plus court, au moins:

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

8
+1 pour un comportement alternatif d'exclusion d'une clé si elle n'est pas en bigdict au lieu de la définir sur None.
dhj

1
Alternativement: dict((k,bigdict.get(k,defaultVal) for k in wanted_keys)si vous devez avoir toutes les clés.
Thomas Andrews,

2
Cette réponse est enregistrée par un "t".
sakurashinken

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

16

Un peu de comparaison de vitesse pour toutes les méthodes mentionnées:

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

Comme prévu, la compréhension des dictionnaires est la meilleure option.


Les 3 premières opérations font une chose différente des deux dernières et entraîneront une erreur si elles keyn'existent pas bigdict.
naught101 Il y a

12

Cette réponse utilise une compréhension du dictionnaire similaire à la réponse sélectionnée, mais ne s'applique que sur un élément manquant.

version python 2:

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

version python 3:

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

2
... mais si le grand dict est ÉNORME, il sera toujours répété complètement (il s'agit d'une opération O (n)), tandis que l'inverse ne ferait que saisir 3 éléments (chacun une opération O (1)).
wouter bolsterlee

1
La question concerne un dictionnaire de seulement 16 touches
Meow

6

Peut être:

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

Python 3 prend même en charge les éléments suivants:

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

Notez que vous pouvez vérifier l'existence dans le dictionnaire comme suit:

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

resp. pour python 3

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

Échoue s'il an'est pas dansbigdict
Håvard S

3

D'accord, c'est quelque chose qui m'a dérangé plusieurs fois, alors merci Jayesh de me le demander.

Les réponses ci-dessus semblent être une aussi bonne solution que n'importe quelle autre, mais si vous l'utilisez partout dans votre code, il est logique d'envelopper la fonctionnalité à mon humble avis. En outre, il existe deux cas d'utilisation possibles ici: un cas où vous vous souciez de savoir si tous les mots clés se trouvent dans le dictionnaire d'origine. et un où vous ne le faites pas. Ce serait bien de traiter les deux également.

Donc, pour mes deux cents, je suggère d'écrire une sous-classe de dictionnaire, par exemple

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

Vous pouvez maintenant extraire un sous-dictionnaire avec

orig_dict.subdict(keywords)

Exemples d'utilisation:

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

Si vous exécutez tout le code ci-dessus, vous devriez voir (quelque chose comme) la sortie suivante (désolé pour la mise en forme):

Dictionnaire d'origine:
{'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}

Dictionnaire à partir de touches impaires:
{'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}

échec de la construction de subd2:
le dictionnaire d'origine ne contient pas de clés

Dictionnaire construit à l'aide de certaines clés incorrectes:
{'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 sous-classe nécessite la conversion d'un objet dict existant en type de sous-classe, ce qui peut être coûteux. Pourquoi ne pas simplement écrire une fonction simple subdict(orig_dict, keys, …)?
musiphil

3

Vous pouvez également utiliser map(ce qui est une fonction très utile pour faire connaissance quand même):

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

Exemple:

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: je l'ai emprunté .get(key, None)à une réponse précédente :)


1

Encore un autre (je préfère la réponse de 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])

son lent pour grand de dict
KXR

0

Solution

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

exemples d'utilisation

# 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}]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.