问题:从Python字典对象中提取键/值对的子集?
我有一个大的字典对象,其中有几个键值对(约16个),但我只对其中的3个感兴趣。什么是最好的方式(最短/最有效/最优雅)?
我所知道的是:
bigdict = {'a':1,'b':2,....,'z':26}
subdict = {'l':bigdict['l'], 'm':bigdict['m'], 'n':bigdict['n']}
我相信还有比这更优雅的方法。有想法吗?
I have a big dictionary object that has several key value pairs (about 16), but I am only interested in 3 of them. What is the best way (shortest/efficient/most elegant) to achieve that?
The best I know is:
bigdict = {'a':1,'b':2,....,'z':26}
subdict = {'l':bigdict['l'], 'm':bigdict['m'], 'n':bigdict['n']}
I am sure there is a more elegant way than this. Ideas?
回答 0
您可以尝试:
dict((k, bigdict[k]) for k in ('l', 'm', 'n'))
…或在 Python 3Python 2.7或更高版本(感谢FábioDiniz指出它也适用于2.7):
{k: bigdict[k] for k in ('l', 'm', 'n')}
更新:正如HåvardS指出的那样,我假设您知道密钥将在字典中- 如果您无法做出此假设,请参阅他的答案。另外,正如timbo在评论中指出的那样,如果您希望将缺少的bigdict
键映射到None
,则可以执行以下操作:
{k: bigdict.get(k, None) for k in ('l', 'm', 'n')}
如果您使用的是Python 3,并且只希望原始字典中实际存在的新字典中的键,则可以使用事实来查看对象实现一些set操作:
{k: bigdict[k] for k in bigdict.keys() & {'l', 'm', 'n'}}
You could try:
dict((k, bigdict[k]) for k in ('l', 'm', 'n'))
… or in Python 3 Python versions 2.7 or later (thanks to Fábio Diniz for pointing that out that it works in 2.7 too):
{k: bigdict[k] for k in ('l', 'm', 'n')}
Update: As Håvard S points out, I’m assuming that you know the keys are going to be in the dictionary – see his answer if you aren’t able to make that assumption. Alternatively, as timbo points out in the comments, if you want a key that’s missing in bigdict
to map to None
, you can do:
{k: bigdict.get(k, None) for k in ('l', 'm', 'n')}
If you’re using Python 3, and you only want keys in the new dict that actually exist in the original one, you can use the fact to view objects implement some set operations:
{k: bigdict[k] for k in bigdict.keys() & {'l', 'm', 'n'}}
回答 1
短一点,至少:
wanted_keys = ['l', 'm', 'n'] # The keys you want
dict((k, bigdict[k]) for k in wanted_keys if k in bigdict)
A bit shorter, at least:
wanted_keys = ['l', 'm', 'n'] # The keys you want
dict((k, bigdict[k]) for k in wanted_keys if k in bigdict)
回答 2
interesting_keys = ('l', 'm', 'n')
subdict = {x: bigdict[x] for x in interesting_keys if x in bigdict}
interesting_keys = ('l', 'm', 'n')
subdict = {x: bigdict[x] for x in interesting_keys if x in bigdict}
回答 3
所有提到的方法的速度比较:
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
不出所料:词典理解是最好的选择。
A bit of speed comparison for all mentioned methods:
UPDATED on 2020.07.13 (thx to @user3780389):
ONLY for keys from bigdict.
IPython 5.5.0 -- An enhanced Interactive Python.
Python 2.7.18 (default, Aug 8 2019, 00:00:00)
[GCC 7.3.1 20180303 (Red Hat 7.3.1-5)] on linux2
import numpy.random as nprnd
...: keys = nprnd.randint(100000, size=10000)
...: bigdict = dict([(_, nprnd.rand()) for _ in range(100000)])
...:
...: %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 {key:bigdict[key] for key in set(keys) & set(bigdict.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: 2.36 ms per loop
100 loops, best of 3: 2.87 ms per loop
100 loops, best of 3: 3.65 ms per loop
100 loops, best of 3: 7.14 ms per loop
1 loop, best of 3: 577 ms per loop
1 loop, best of 3: 563 ms per loop
As it was expected: dictionary comprehensions are the best option.
回答 4
该答案使用与所选答案相似的字典理解,但除了缺失项外,不会。
python 2版本:
{k:v for k, v in bigDict.iteritems() if k in ('l', 'm', 'n')}
python 3版本:
{k:v for k, v in bigDict.items() if k in ('l', 'm', 'n')}
This answer uses a dictionary comprehension similar to the selected answer, but will not except on a missing item.
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')}
回答 5
也许:
subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n']])
Python 3甚至支持以下内容:
subdict={a:bigdict[a] for a in ['l','m','n']}
请注意,您可以按照以下步骤检查字典中是否存在:
subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n'] if x in bigdict])
分别 对于python 3
subdict={a:bigdict[a] for a in ['l','m','n'] if a in bigdict}
Maybe:
subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n']])
Python 3 even supports the following:
subdict={a:bigdict[a] for a in ['l','m','n']}
Note that you can check for existence in dictionary as follows:
subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n'] if x in bigdict])
resp. for python 3
subdict={a:bigdict[a] for a in ['l','m','n'] if a in bigdict}
回答 6
好的,这让我有些困扰,所以谢谢Jayesh提出这个问题。
上面的答案似乎是一个很好的解决方案,但是如果您在整个代码中都使用此解决方案,则包装功能恕我直言是有意义的。另外,这里有两种可能的用例:一种在用例中,您在乎所有关键字是否都在原始词典中。还有一个你不知道的地方 平等地对待这两者将是很好的。
因此,对于我的两分钱价值,我建议编写一个字典的子类,例如
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
现在,您可以使用
orig_dict.subdict(keywords)
用法示例:
#
## 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")
如果运行以上所有代码,则应该看到(类似)以下输出(对不起格式):
原始字典:
{‘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}
奇数键字典:
{‘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}
subd2构造失败:
原始词典不包含某些键
使用一些错误键构造的字典:
{‘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}
Okay, this is something that has bothered me a few times, so thank you Jayesh for asking it.
The answers above seem like as good a solution as any, but if you are using this all over your code, it makes sense to wrap the functionality IMHO. Also, there are two possible use cases here: one where you care about whether all keywords are in the original dictionary. and one where you don’t. It would be nice to treat both equally.
So, for my two-penneth worth, I suggest writing a sub-class of dictionary, e.g.
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
Now you can pull out a sub-dictionary with
orig_dict.subdict(keywords)
Usage examples:
#
## 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")
If you run all the above code, you should see (something like) the following output (sorry for the formatting):
Original dictionary:
{‘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}
Dictionary from odd numbered keys:
{‘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}
subd2 construction fails:
original dictionary doesn’t contain some keys
Dictionary constructed using some bad keys:
{‘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}
回答 7
您还可以使用map
(无论如何,这都是非常有用的功能):
sd = dict(map(lambda k: (k, l.get(k, None)), l))
例:
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:我.get(key, None)
从以前的答案中借来了:)
You can also use map
(which is a very useful function to get to know anyway):
sd = dict(map(lambda k: (k, l.get(k, None)), l))
Example:
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: I borrowed the .get(key, None)
from a previous answer :)
回答 8
还有一个(我更喜欢马克·朗伊尔的回答)
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])
Yet another one (I prefer Mark Longair’s answer)
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])
回答 9
解
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`')
使用实例
# 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}]
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`')
examples of use
# 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}]