我正在使用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作为值。不幸的是,它没有加快查找过程(这里是更新的代码 - 我翻转了k
和v
edgeStartPoint
和edgeEndPoint
):
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))
答案 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循环中,因此对于每个边缘:
因此,对于您的数据,您应该获得大约100000 *(100000 + 90000 + 90000)或O(n ^ 2)次操作,这不仅仅是一次通过边缘(100000或O(n))