Teilmenge von Schlüssel-Wert-Paaren aus dem Python-Wörterbuchobjekt extrahieren?

313

Ich habe ein großes Wörterbuchobjekt mit mehreren Schlüsselwertpaaren (ungefähr 16), aber ich interessiere mich nur für 3 davon. Was ist der beste Weg (kürzester / effizienter / elegantester), um dies zu erreichen?

Das Beste, was ich weiß, ist:

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

Ich bin sicher, es gibt einen eleganteren Weg als diesen. Ideen?

Jayesh
quelle

Antworten:

430

Du könntest es versuchen:

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

... oder in Python 3Python-Versionen 2.7 oder höher (danke an Fábio Diniz für den Hinweis, dass es auch in 2.7 funktioniert) :

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

Update: Wie Håvard S betont, gehe ich davon aus, dass Sie wissen, dass die Schlüssel im Wörterbuch enthalten sein werden - siehe seine Antwort, wenn Sie diese Annahme nicht treffen können. Alternativ kann , wie timbo weist darauf hin , in den Kommentaren, wenn Sie einen Schlüssel möchten , die in fehlt bigdictbis zur Karte Nonekönnen Sie tun:

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

Wenn Sie Python 3 verwenden und nur Schlüssel in dem neuen Diktat möchten, die tatsächlich im ursprünglichen vorhanden sind, können Sie die Tatsache verwenden, um Objekte anzuzeigen, die einige festgelegte Operationen implementieren:

{k: bigdict[k] for k in bigdict.keys() & {'l', 'm', 'n'}}
Mark Longair
quelle
5
Wird scheitern, wenn bigdictnicht enthältk
Håvard S
7
Ein bisschen hart, um das herabzustimmen - es schien mir aus dem Zusammenhang ziemlich klar, dass es bekannt ist, dass diese Schlüssel im Wörterbuch sind ...
Mark Longair
9
{k: bigdict.get(k,None) for k in ('l', 'm', 'n')}wird mit der Situation umgehen, in der ein angegebener Schlüssel im
Quellwörterbuch
9
@MarkLongair Abhängig vom Anwendungsfall {k: bigdict [k] für k in ('l', 'm', 'n'), wenn k in bigdict} möglicherweise besser ist, da nur die Schlüssel gespeichert werden, die tatsächlich Werte haben.
Briford Wylie
6
bigdict.keys() & {'l', 'm', 'n'} ==> bigdict.viewkeys() & {'l', 'm', 'n'} für Python2.7
kxr
119

Zumindest etwas kürzer:

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.
quelle
8
+1 für alternatives Verhalten beim Ausschließen eines Schlüssels, wenn dieser nicht in bigdict ist, anstatt ihn auf None zu setzen.
Dhj
1
Alternativ: dict((k,bigdict.get(k,defaultVal) for k in wanted_keys)Wenn Sie alle Schlüssel haben müssen.
Thomas Andrews
2
Diese Antwort wird durch ein "t" gespeichert.
Sakurashinken
24
interesting_keys = ('l', 'm', 'n')
subdict = {x: bigdict[x] for x in interesting_keys if x in bigdict}
theheadofabroom
quelle
16

Ein kleiner Geschwindigkeitsvergleich für alle genannten Methoden:

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

Wie erwartet: Wörterbuchverständnisse sind die beste Option.

Sklavit
quelle
Die ersten drei Operationen machen eine andere Sache als die letzten beiden und führen zu einem Fehler, wenn keysie nicht vorhanden sind bigdict.
naught101 vor
12

Diese Antwort verwendet ein Wörterbuchverständnis, das der ausgewählten Antwort ähnelt, jedoch nur für ein fehlendes Element gilt.

Python 2-Version:

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

Python 3-Version:

{k:v for k, v in bigDict.items() if k in ('l', 'm', 'n')}
Miau
quelle
2
... aber wenn das große Diktat RIESIG ist, wird es immer noch vollständig wiederholt (dies ist eine O (n) -Operation), während die Umkehrung nur 3 Elemente erfasst (jeweils eine O (1) -Operation).
Wouter Bolsterlee
1
Die Frage ist über ein Wörterbuch mit nur 16 Schlüsseln
Meow
6

Vielleicht:

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

Python 3 unterstützt sogar Folgendes:

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

Beachten Sie, dass Sie die Existenz im Wörterbuch wie folgt überprüfen können:

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

bzw. für Python 3

subdict={a:bigdict[a] for a in ['l','m','n'] if a in bigdict}
Phimuemue
quelle
Schlägt fehl, wenn anicht inbigdict
Håvard S
3

Okay, das hat mich ein paar Mal gestört, also danke Jayesh, dass du es gefragt hast.

Die obigen Antworten scheinen eine ebenso gute Lösung zu sein wie jede andere, aber wenn Sie dies im gesamten Code verwenden, ist es sinnvoll, die Funktionalität IMHO einzuschließen. Außerdem gibt es hier zwei mögliche Anwendungsfälle: einen, bei dem es Ihnen wichtig ist, ob alle Schlüsselwörter im Originalwörterbuch enthalten sind. und eine, wo du nicht. Es wäre schön, beide gleich zu behandeln.

Für meinen Zwei-Penneth-Wert schlage ich vor, eine Unterklasse von Wörterbüchern zu schreiben, z

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

Jetzt können Sie mit ein Unterwörterbuch herausziehen

orig_dict.subdict(keywords)

Anwendungsbeispiele:

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

Wenn Sie den gesamten obigen Code ausführen, sollte (ungefähr) die folgende Ausgabe angezeigt werden (Entschuldigung für die Formatierung):

Originalwörterbuch:
{'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}

Wörterbuch aus ungeradzahligen Schlüsseln:
{'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}

Die Subd2-Konstruktion schlägt fehl:
ursprüngliche Wörterbuch enthält keine Schlüssel

Wörterbuch, das mit einigen schlechten Schlüsseln erstellt wurde:
{'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
quelle
1
Für die Unterklasse muss ein vorhandenes Diktierobjekt in den Unterklassentyp konvertiert werden, was teuer sein kann. Warum nicht einfach eine einfache Funktion schreiben subdict(orig_dict, keys, …)?
Musiphil
3

Sie können auch verwenden map(was eine sehr nützliche Funktion ist, um sie trotzdem kennenzulernen):

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

Beispiel:

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: Ich habe das .get(key, None)aus einer früheren Antwort ausgeliehen :)

Halfdanrump
quelle
1

Noch eine (ich bevorzuge Mark Longairs Antwort)

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
quelle
seine langsame für große dict ist
kxr
0

Lösung

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

Anwendungsbeispiele

# 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
quelle