【问题标题】:A faster way of comparing two lists of point-tuples?比较两个点元组列表的更快方法?
【发布时间】:2011-06-08 01:06:15
【问题描述】:

我有两个列表(长度可能相同也可能不同)。在每个列表中,是一系列由两个点组成的元组(基本上是 X、Y 值)。

我将这两个列表相互比较,以找到具有相似点值的两个点。我尝试过列表理解技术,但它与列表中的嵌套元组真的很混淆,我无法让它工作。

这是最好(最快)的方法吗?我觉得可能有一种更 Pythonic 的方式来做到这一点。

假设我有两个列表:

pointPairA = [(2,1), (4,8)]
pointPairB = [(3,2), (10,2), (4,2)]

然后是一个用于存储对的空列表和一个仅存储匹配对的容差值

matchedPairs = []
tolerance = 2

然后这个循环解包元组,比较差异,并将它们添加到matchedPairs列表中以指示匹配。

for pointPairA in pointPairListA:
    for pointPairB in pointPairListB:
        ## Assign the current X,Y values for each pair
        pointPairA_x, pointPairA_y = pointPairA
        pointPairB_x, pointPairB_x = pointPairB

        ## Get the difference of each set of points
        xDiff = abs(pointPairA_x - pointPairB_x)
        yDiff = abs(pointPairA1_y - pointPairB_y)

        if xDiff < tolerance and yDiff < tolerance:
            matchedPairs.append((pointPairA, pointPairB))

这将导致matchedPairs看起来像这样,里面有两个点元组的元组:

[( (2,1), (3,2) ), ( (2,1), (4,2) )]

【问题讨论】:

  • 如果您可以使用“距离”而不是平方作为公差,您可以使用复数而不是元组,例如。 [2+1j, 4+8j]。然后您可以将abs(pt1-pt2) 与容差进行比较

标签: python list-comprehension


【解决方案1】:

这里 pointpairA 是单个列表,而 pointpairB 将是 20k 列表之一

from collections import defaultdict
from itertools import product

pointPairA = [(2,1), (4,8)]
pointPairB = [(3,2), (10,2), (4,2)]
tolerance = 2

dA = defaultdict(list)
tolrange = range(-tolerance, tolerance+1)
for pA, dx, dy in product(pointPairA, tolrange, tolrange):
    dA[pA[0]+dx,pA[1]+dy].append(pA)

# you would have a loop here though the 20k lists
matchedPairs = [(pA, pB) for pB in pointPairB for pA in dA[pB]]  

print matchedPairs

【讨论】:

    【解决方案2】:

    如果这些列表很大,我建议找到更快的算法...

    我将首先按对中 (x,y) 的总和对两个对列表进行排序。 (因为只有当它们的和接近时,两点才能接近。)

    对于第一个列表中的任何点,这将严重限制您需要在第二个列表中搜索的范围。跟踪第二个列表上的“滑动窗口”,对应于总和在第一个列表的当前元素之和的2*tolerance 范围内的元素。 (其实只需要跟踪滑动窗口的开始...)

    假设 tolerance 相当小,这应该将您的 O(n^2) 操作转换为 O(n log n)。

    【讨论】:

    • 对不起,我没有提到,列表并不大。事实上,目前,它们的长度永远不会超过 15 个元组,而且其中大多数长度约为 14 个。
    【解决方案3】:

    使用列表理解:

    [(pa, pb) for pa in pointPairA for pb in pointPairB \
              if abs(pa[0]-pb[0]) <= tolerance and abs(pa[1]-pb[1]) <= tolerance]
    

    比你的循环快一点:

    (for 1 million executions)
    
    >>> (list comprehension).timeit()
    2.1963138580322266 s
    
    >>> (your method).timeit()
    2.454944133758545 s
    

    【讨论】:

    • 我知道我做错了什么,谢谢你的例子。这正是我需要的一个班轮。稍微快一点,我相信这会加起来:我有一个列表,我将其与其他 2 万个列表进行比较。
    • @STH,由于您将一个列表与其他 20k 个列表进行比较,因此花一些时间从一个列表中创建一个 dict 或集合以允许真正快速查找 20k 个其他列表可能是有意义的。这些值总是整数吗?对于 2 的容差,dict 将是列表大小的 25 倍,但 20k 比较将是 O(N)
    • @gnibbler 你的意思是让第一个列表成为字典或集合,而不是 20k 其他列表,对吗?这些值将始终是整数。这 20k 个列表在被腌制后存储在 MySQL 数据库中。
    • 只是想让你知道我在这里的速度差异。转成单线程后,整个函数运行一次的速度是1.80s,现在是0.87s。
    • @STH,是的,我现在已经在答案中提供了这个
    猜你喜欢
    • 2013-11-19
    • 1970-01-01
    • 2017-09-01
    • 2011-02-24
    • 2014-04-28
    • 1970-01-01
    • 2016-09-04
    • 2015-07-04
    • 1970-01-01
    相关资源
    最近更新 更多