加快查找两个词典之间的匹配(Python)

时间:2015-02-27 06:50:45

标签: python list dictionary list-comprehension

我正在使用Python 2.7处理空间分析问题。我有一个字典edges表示图中的边,其中key是edgeID,值是起点/终点:

{e1: [(12.8254, 55.3880), (12.8343, 55.3920)], 
e2: [(12.8254, 55.3880), (12.8235, 55.3857)], 
e3: [(12.2432, 57.1120), (12.2426, 57.1122)]}

我有另一个字典nodes,其中key是nodeID,值是节点坐标:

{n14: (12.8254, 55.3880), 
n15: (12.8340, 55.3883), 
n16: (12.8235, 55.3857), 
n17: (12.8343, 55.3920)}

我需要得到一个看起来像的列表(键中的'n'和'e'仅用于此问题的说明目的,我有整数):

[(e1,n14,n17),(e2,n14,n16)..]

也就是说,我遍历边缘dict,取每个键,找到nodes dict中存在的值并添加到元组。这就是我现在的做法:

edgesList = []
for featureId in edges:
        edgeFeatureId = [k for k, v in edges.iteritems() if k == featureId][0]
        edgeStartPoint = [k for k, v in nodes.iteritems() if v == edges[featureId][0]][0]#start point
        edgeEndPoint = [k for k, v in nodes.iteritems() if v == edges[featureId][1]][0]#end point
        edgesList.append((edgeFeatureId,edgeStartPoint,edgeEndPoint))

这是有效的,但在处理大型数据集时非常慢(100K边缘和90K节点需要大约10分钟)。

我已经弄清楚如何在获取每个元组的项目时使用列表推导,但是有可能将我的3个列表推导理解为一个以避免使用for循环迭代边缘(如果这样会加快速度吗?

还有其他方法可以更快地构建这样的列表吗?

更新

正如马丁建议的那样,我已将我的节点颠倒了:

nodesDict = dict((v,k) for k,v in oldnodesDict.iteritems())

将节点坐标元组作为键,将nodeID作为值。不幸的是,它没有加快查找过程(这里是更新的代码 - 我翻转了kv edgeStartPointedgeEndPoint):

edgesList = []
for featureId in edges:
        edgeFeatureId = [k for k, v in edges.iteritems() if k == featureId][0]
        edgeStartPoint = [v for k, v in nodes.iteritems() if k == edges[featureId][0]][0]#start point
        edgeEndPoint = [v for k, v in nodes.iteritems() if k == edges[featureId][1]][0]#end point
        edgesList.append((edgeFeatureId,edgeStartPoint,edgeEndPoint))

3 个答案:

答案 0 :(得分:2)

由于您根据坐标进行匹配,因此您的节点字典应该反转。

也就是说,它应该是这样的:

{(12.8254, 55.3880): n14, 
(12.8340, 55.3883): n15, 
(12.8235, 55.3857): n16, 
(12.8343, 55.3920): n17}

这样,当您在边缘上进行迭代时,您可以快速查找相应的节点:

edgesList = []
for featureId in edges:
    coordinates = edges[featureId]
    c0, c1 = coordinates

    n0 = nodes[c0]
    n1 = nodes[c1]

    edgesList.append((featureId, n0, n1))

请记住,字典可以非常快速地找到任何给定键的相应值。如此快速,在一般情况下,查找速度should barely change,因为字典大小为1或大小为100万。

答案 1 :(得分:1)

正如您的评论中所述,问题是最后一次操作edgesList.append((id,start,end))

这似乎是一个数据类型问题:一个大字典因设计而变慢。看看here

但很高兴您可以使用双端队列(deque)代替。 deque documentation:" Deques支持线程安全,内存有效的追加和双端队列的弹出,在任一方向上具有大致相同的O(1)性能。"

在代码中它意味着你初始化一个deque并以高性能附加到它。

edgesList = deque() 
for featureId in edges:
        edgeFeatureId = [k for k, v in edges.iteritems() if k == featureId][0]
        edgeStartPoint = [v for k, v in nodes.iteritems() if k == edges[featureId][0]][0]#start point
        edgeEndPoint = [v for k, v in nodes.iteritems() if k == edges[featureId][1]][0]#end point
        edgesList.append((edgeFeatureId,edgeStartPoint,edgeEndPoint))

答案 2 :(得分:1)

根据您的示例数据,这里是我认为可行的示例:

edges = {
    1: [(12.8254, 55.3880), (12.8343, 55.3920)],
    2: [(12.8254, 55.3880), (12.8235, 55.3857)],
    3: [(12.2432, 57.1120), (12.2426, 57.1122)]}
nodes = {
    14: (12.8254, 55.3880),
    15: (12.8340, 55.3883),
    16: (12.8235, 55.3857),
    17: (12.8343, 55.3920)}
reverseNodes=dict((v,k) for k, v in nodes.iteritems())
edgesList=[]
for k,v in edges.items():
    edgesList.append( 
            (k,
             reverseNodes.get(v[0], -1),
             reverseNodes.get(v[1], -1)))

也许我对你构建edgesList时有些不理解,但我认为这看起来更简单,更快。

再次根据您的示例代码,这就是消耗您的cpu-time的时间:

edgeFeatureId = [k for k, v in edges.iteritems() if k == featureId][0]
edgeStartPoint = [v for k, v in nodes.iteritems() if k == edges[featureId][0]][0]#start point
edgeEndPoint = [v for k, v in nodes.iteritems() if k == edges[featureId][1]][0]#end point

这存在于for循环中,因此对于每个边缘:

  • 在边缘列表上迭代一次额外的时间(以找到您已经拥有的边缘ID)
  • 在节点列表上迭代两次以查找起点和终点(因为我们想出了如何使用reverseNodes-dict进行直接查找,所以不再需要它了。)

因此,对于您的数据,您应该获得大约100000 *(100000 + 90000 + 90000)或O(n ^ 2)次操作,这不仅仅是一次通过边缘(100000或O(n))