比较两个大词典并为它们共有的键创建值列表

时间:2016-04-14 16:16:09

标签: python dictionary

我有两个词典,如:

dict1 = { (1,2) : 2, (2,3): 3, (1,3): 3}
dict2 = { (1,2) : 1, (1,3): 2}

我想要的输出是两个字典中存在的项目的两个值列表:

[2,3]
[1,2]

我现在正在做的事情是这样的:

list1 = []
list2 = []

for key in dict1.keys():
    if key in dict2.keys():
        list1.append(dict1.get(key))
        list2.append(dict2.get(key))

此代码运行时间太长,这不是我期待的。我想知道是否有更有效的方法可以做到这一点?

4 个答案:

答案 0 :(得分:28)

commons = set(dict1).intersection(set(dict2))
list1 = [dict1[k] for k in commons]
list2 = [dict2[k] for k in commons]

答案 1 :(得分:14)

不要使用dict.keys。在python2.x上,它每次调用时都会创建一个新列表(这是O(N)操作 - list.__contains__平均是另一个O(N)操作。只需依赖字典是可迭代容器的事实(使用O(1)查找):

list1 = []
list2 = []

for key in dict1:
    if key in dict2:
        list1.append(dict1.get(key))
        list2.append(dict2.get(key))

请注意,在python2.7上,您可以使用viewkeys直接获取交集:

>>> a = {'foo': 'bar', 'baz': 'qux'}
>>> b = {'foo': 'bar'}
>>> a.viewkeys() & b
set(['foo'])

(在python3.x上,你可以在这里使用keys而不是viewkeys

for key in dict1.viewkeys() & dict2:
    list1.append(dict1[key]))
    list2.append(dict2[key]))

答案 2 :(得分:4)

您可以在zip()函数中使用列表推导:

>>> vals1, vals2 = zip(*[(dict1[k], v) for k, v in dict2.items() if k in dict1])
>>> 
>>> vals1
(2, 3)
>>> vals2
(1, 2)

或者作为使用视图对象和operator.itemgetter()的更实用的方法,您可以这样做:

>>> from operator import itemgetter
>>> intersect = dict1.viewkeys() & dict2.viewkeys()
>>> itemgetter(*intersect)(dict1)
(2, 3)
>>> itemgetter(*intersect)(dict2)
(1, 2)

已接受答案的基准:

from timeit import timeit


inp1 = """
commons = set(dict1).intersection(set(dict2))
list1 = [dict1[k] for k in commons]
list2 = [dict2[k] for k in commons]
   """

inp2 = """
zip(*[(dict1[k], v) for k, v in dict2.items() if k in dict1])
   """
inp3 = """
intersect = dict1.viewkeys() & dict2.viewkeys()
itemgetter(*intersect)(dict1)
itemgetter(*intersect)(dict2)
"""
dict1 = {(1, 2): 2, (2, 3): 3, (1, 3): 3}
dict2 = {(1, 2): 1, (1, 3): 2}
print 'inp1 ->', timeit(stmt=inp1,
                        number=1000000,
                        setup="dict1 = {}; dict2 = {}".format(dict1, dict2))
print 'inp2 ->', timeit(stmt=inp2,
                        number=1000000,
                        setup="dict1 = {}; dict2 = {}".format(dict1, dict2))
print 'inp3 ->', timeit(stmt=inp3,
                        number=1000000,
                        setup="dict1 = {}; dict2 = {};from operator import itemgetter".format(dict1, dict2))

输出:

inp1 -> 0.000132083892822
inp2 -> 0.000128984451294
inp3 -> 0.000160932540894

对于长度为10000的字典和随机生成的项目,在100循环中使用:

inp1 -> 1.18336105347
inp2 -> 1.00519990921
inp3 -> 1.52266311646

编辑:

正如@Davidmh在评论中提到的拒绝为第二种方法引发异常时,您可以将代码包装在try-except表达式中:

try:
    intersect = dict1.viewkeys() & dict2.viewkeys()
    vals1 = itemgetter(*intersect)(dict1)
    vals2 = itemgetter(*intersect)(dict2)
except TypeError:
    vals1 = vals2 = []

答案 3 :(得分:0)

这应该在python3中使用keys并在python2中使用viewkeys。这些视图对象的行为类似于集合,并且不需要额外的工作来构建它们......它们只是底层字典键的“视图”。这样就可以保存set个对象的构造。

common = dict1.viewkeys() & dict2.viewkeys()
list1 = [dict1[k] for k in common]
list2 = [dict2[k] for k in common]

dict_views个对象可以直接与字典相交,因此以下代码也可以正常工作。我更喜欢上一个样本。

common = dict1.viewkeys() & dict2