¿Extraer subconjunto de pares clave-valor del objeto del diccionario Python?


313

Tengo un gran objeto de diccionario que tiene varios pares de valores clave (alrededor de 16), pero solo estoy interesado en 3 de ellos. ¿Cuál es la mejor manera (más corta / eficiente / más elegante) para lograr eso?

Lo mejor que sé es:

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

Estoy seguro de que hay una forma más elegante que esta. Ideas?

Respuestas:


430

Tu podrías intentar:

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

... o en Python 3Python versiones 2.7 o posterior (gracias a Fábio Diniz por señalar que también funciona en 2.7) :

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

Actualización: como señala Håvard S , supongo que usted sabe que las claves estarán en el diccionario; vea su respuesta si no puede hacer esa suposición. Alternativamente, como señala Timbo en los comentarios, si desea una clave que falta en el bigdictmapa None, puede hacer lo siguiente:

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

Si está utilizando Python 3, y solo quiere claves en el nuevo dict que realmente existe en el original, puede usar el hecho para ver los objetos para implementar algunas operaciones de conjunto:

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

55
Fallará si bigdictno contienek
Håvard S

77
Un poco duro para desestimar eso: me pareció bastante claro por el contexto que se sabe que estas claves están en el diccionario ...
Mark Longair

99
{k: bigdict.get(k,None) for k in ('l', 'm', 'n')}se ocupará de la situación en la que falta una clave específica en el diccionario fuente configurando la clave en el nuevo dict a Ninguno
timbo

99
@MarkLongair Dependiendo del caso de uso {k: bigdict [k] para k in ('l', 'm', 'n') si k in bigdict} podría ser mejor, ya que solo almacena las claves que realmente tienen valores.
Briford Wylie

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

119

Un poco más corto, al menos:

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

8
+1 para el comportamiento alternativo de excluir una clave si no está en bigdict en lugar de establecerla en None.
dhj

1
Alternativamente: dict((k,bigdict.get(k,defaultVal) for k in wanted_keys)si debe tener todas las claves.
Thomas Andrews

2
Esta respuesta se guarda con una "t".
sakurashinken

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

16

Un poco de comparación de velocidad para todos los métodos mencionados:

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

Como era de esperar: la mejor opción es la comprensión del diccionario.


Las primeras 3 operaciones están haciendo algo diferente a las dos últimas, y resultará en un error si keyno existe bigdict.
naught101 hace

12

Esta respuesta utiliza una comprensión del diccionario similar a la respuesta seleccionada, pero no lo hará excepto en un elemento que falta.

versión de python 2:

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

versión de python 3:

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

2
... pero si el gran dict es ENORME, todavía se repetirá por completo (esta es una operación O (n)), mientras que el inverso solo tomaría 3 elementos (cada uno una operación O (1)).
wouter bolsterlee

1
La pregunta es sobre un diccionario de solo 16 teclas
Meow

6

Tal vez:

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

Python 3 incluso admite lo siguiente:

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

Tenga en cuenta que puede verificar la existencia en el diccionario de la siguiente manera:

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

resp. para python 3

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

Falla si ano está enbigdict
Håvard S

3

Bien, esto es algo que me ha molestado varias veces, así que gracias Jayesh por preguntarlo.

Las respuestas anteriores parecen ser una solución tan buena como cualquier otra, pero si está usando esto en todo su código, tiene sentido envolver la funcionalidad en mi humilde opinión. Además, aquí hay dos casos de uso posibles: uno en el que le importa si todas las palabras clave están en el diccionario original. y uno donde no lo hagas. Sería bueno tratar a ambos por igual.

Entonces, para mi valor de dos peniques, sugiero escribir una subclase de diccionario, por ejemplo

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

Ahora puede sacar un sub-diccionario con

orig_dict.subdict(keywords)

Ejemplos de uso:

#
## 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 ejecuta todo el código anterior, debería ver (algo así) el siguiente resultado (perdón por el formato):

Diccionario original:
{'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}

Diccionario de teclas impares:
{'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}

la construcción subd2 falla:
diccionario original no contiene algunas claves

Diccionario construido con algunas claves incorrectas:
{'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 subclase requiere que un objeto dict existente se convierta en el tipo de subclase, lo que puede ser costoso. ¿Por qué no simplemente escribir una función simple subdict(orig_dict, keys, …)?
musiphil

3

También puede usar map(que es una función muy útil para conocer de todos modos):

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

Ejemplo:

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

PD: tomé prestado el .get(key, None)de una respuesta anterior :)


1

Otra más (prefiero la respuesta 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])

es lento para grandes dict's
kxr

0

solución

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

ejemplos de uso

# 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}]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.