Python:匹配列表还是匹配字典,哪个更快?

Python:匹配列表还是匹配字典,哪个更快?,python,list,dictionary,match,Python,List,Dictionary,Match,我有两个大名单。其中每一项都由以下列表组成: list_1 = [[1, "BMW", "Boston", "01Jan2013"], [37, "Chevrolet", "Denver", "05Jan2013"], [854, "BMW", "Boston", "01Jan2013"],...] list_2 = [[1, "Mercedes", "Boston", "01Jan2013"], [37, "Chevrolet", "Denver", "05Jan2013"], [854,

我有两个大名单。其中每一项都由以下列表组成:

list_1 = [[1, "BMW", "Boston", "01Jan2013"], [37, "Chevrolet", "Denver", "05Jan2013"],
[854, "BMW", "Boston", "01Jan2013"],...]

list_2 = [[1, "Mercedes", "Boston", "01Jan2013"], [37, "Chevrolet", "Denver", "05Jan2013"],
[854, "Toyota", "Boston", "01Jan2013"],...]
内部列表始终具有相同的项目类型

现在,我想使用内部列表的项目1、项目3和项目4,将
list_1
中的每个内部列表与
list_2
中的每个内部列表进行匹配。即:序列号、城市来源和日期。这些键始终与列表2相同。
列表_1
中的内部列表在
列表_2
中只能有0或1个匹配项


做这件事的最快方法是什么?是否要将列表转换为字典?

您可以定义一个键函数,指定要比较的字段:

def item_key(item):
    return tuple(item[i] for i in [0, 2, 3])
它应该是可散列的,以便您可以将其用作dict键。 您可以创建从项目键到项目本身的映射,或者如果不同项目可以共享同一个键,则可以创建项目列表

key_to_item2 = dict((item_key(item), item) for item in list2)
现在,您可以对照dict测试列表1中的每个项目

for item1 in list1:
    item2 = key_to_item2.get(item_key(item1))
    if item2 is None:
        # no match found
    else:
        # item2 in list2 matches item1 in list1

这种方法可以很容易地调整为使用其他字段进行匹配和支持多个匹配。

就速度而言,您可能需要使用字典。看起来,不管怎样,您都需要遍历一个列表。字典当然比遍历列表要快,所以你至少可以将一个列表编成一个dict(我测试了以下解决方案,在两个单独的列表中有200000个条目,就像你的一样,我的速度平均为0.109999秒。列表远远超出了这一标准。)如果您只是尝试使用列表或元组,您可能不会接近这一点,除非条目的顺序允许您使用类似zip的东西。您的序列号似乎是唯一的,因此以下方法可行(通过迭代一个列表,然后将项目1、3和4[位置0、2和3]与字典中的值进行比较):

将第二个列表转换为字典后,只需迭代一个列表即可获得结果。此解决方案将返回列表_1中每个匹配项的整个子列表,这似乎是您想要的。如果您希望从两个列表中获得完整的匹配子列表,这将起作用:

for item in list_1:
    if dict_2[item[0]][1:] == item[2:]:
        print item, [item[0]] + dict_2[item[0]]


[1, 'BMW', 'Boston', '01Jan2013'] [1, 'Mercedes', 'Boston', '01Jan2013']
[37, 'Chevrolet', 'Denver', '05Jan2013'] [37, 'Chevrolet', 'Denver', '05Jan2013']
[854, 'BMW', 'Boston', '01Jan2013'] [854, 'Toyota', 'Boston', '01Jan2013']
什么是“最佳”解决方案取决于您想要什么。如果是关于速度,那么根据输入的大小,字典可能是最好的选择

如果是关于清晰和简洁,我认为保持列表并做以下事情是非常有益的:

result = []
for l1 in list1:
    result.append([l2 for l2 in list2
                   if l1[0] == l2[0] and l1[2] == l2[2] and l1[3] == l2[3]])
    assert(len(result[-1]) in [0,1])

假设
list_1
不能包含重复项(根据您的评论),您可以将其转换为
元组的
集合,而不是列表。这样,您就可以使用
in
操作符有效地检查集合中是否有某个项目


您需要使用元组而不是列表,因为列表是可变的(因此不可散列),所以不能将它们放入
集中。如果您使用的是
dict
,则同样适用,但是
集合
似乎更适合您的用例(不清楚您将使用什么作为
dict
的键)。

类似于@omz的解决方案是将列表转换为字典。dict键是元组
(序列、城市、日期)
(项目1、3和4),值是另一个字段“make”(项目2)。然后,为了匹配它们,只需遍历
list_1
(现在是
dict_1
)的键,尝试检索
dict_2
的相应成员:

dict_1 = {(1, "Boston", "01Jan2013"):"BMW", (37, "Denver", "05Jan2013"):"Chevrolet", (854, "Boston", "01Jan2013"):"BMW",...}

dict_2 = {(1, "Boston", "01Jan2013"):"Mercedes", (37, "Denver", "05Jan2013"):"Chevrolet", (854, "Boston", "01Jan2013"):"Toyota",...}

for k in dict_1:
    match = dict_2.get (k, None)
    if match is not None:
        print "Match found:", match
    else:
        print "No match"

当然,当你找到一个匹配项时,你所做的不会是我写的,但这应该可以用来显示你如何找到匹配项。

是否可以
列表1
包含重复项?元组是不可变的?请澄清。@JoSo一旦创建元组,就不能更改元组(例如添加项)。这使得它们可用作
dict
键或
集合的一部分
t=(1,2,3);s=t;t+=(4,);打印s
——如您所见,原始元组没有改变,您的代码通过使用
t
中包含的值初始化元组,并与
4
连接,然后重新分配变量
t
来创建一个新元组。另外,明确声明元组是不可变的。Python中没有
where
关键字,您可能是指
if
dict_1 = {(1, "Boston", "01Jan2013"):"BMW", (37, "Denver", "05Jan2013"):"Chevrolet", (854, "Boston", "01Jan2013"):"BMW",...}

dict_2 = {(1, "Boston", "01Jan2013"):"Mercedes", (37, "Denver", "05Jan2013"):"Chevrolet", (854, "Boston", "01Jan2013"):"Toyota",...}

for k in dict_1:
    match = dict_2.get (k, None)
    if match is not None:
        print "Match found:", match
    else:
        print "No match"