我已经确定了以下代码片段执行效果非常糟糕的两个可能原因,因为points是10000个size-2列表的列表。
邻居地图字典。
def calculate_distance(point1, point2):
a = (point1[0], point1[1])
b = (point2[0], point2[1])
return distance.euclidean(a, b)
def get_eps_neighbours(points, eps):
neighbours = {}
index = 0
for p in points:
for q in points:
if(calculate_distance(p, q) <= eps):
if index in neighbours:
neighbours[index].append(q)
else:
neighbours[index] = q
index = index + 1
return {'neighbours': neighbours}
关于如何提高代码效率的任何建议?
答案 0 :(得分:2)
这是平凡并行问题的一个例子。
我的建议:
numpy
x
另一个用于y
numpy
的数组算术示例:
In [52]: points = [(1,1), (2,2), (3,3), (4,4)] # super-simple data
In [54]: Xb = numpy.repeat(numpy.array(points)[:,0], 4).reshape(4, 4)
In [60]: Xb
Out[60]:
array([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4]])
In [61]: Xa = numpy.tile(numpy.array(points)[:,0], 4).reshape(4, 4)
In [62]: Xa
Out[62]:
array([[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]])
# Yb = numpy.repeat(numpy.array(points)[:,1], 4).reshape(4, 4)
# Ya = numpy.tile(numpy.array(points)[:,1], 4).reshape(4, 4)
In [65]: D = ((Xa - Xb) ** 2 + (Ya - Yb) ** 2) ** 0.5
In [66]: D
Out[66]:
array([[ 0. , 1.41421356, 2.82842712, 4.24264069],
[ 1.41421356, 0. , 1.41421356, 2.82842712],
[ 2.82842712, 1.41421356, 0. , 1.41421356],
[ 4.24264069, 2.82842712, 1.41421356, 0. ]])
In [71]: D < 2
Out[71]:
array([[ True, True, False, False],
[ True, True, True, False],
[False, True, True, True],
[False, False, True, True]], dtype=bool)
# Assuming you want only one copy from each pair (a,b), (b,a)
In [73]: triangle = numpy.tri(4, 4, -1, bool)
In [74]: triangle
Out[74]:
array([[False, False, False, False],
[ True, False, False, False],
[ True, True, False, False],
[ True, True, True, False]], dtype=bool)
In [76]: neighbours = (D < 2) * triangle # multiplication for "logical and"
Out[76]:
array([[False, False, False, False],
[ True, False, False, False],
[False, True, False, False],
[False, False, True, False]], dtype=bool)
# Neighbours' x and y coordinates are available so:
In [107]: numpy.compress(neighbours.flatten(), Xa.flatten())
Out[107]: array([1, 2, 3])
# Indices to elements in original `points` list like this:
Indexb = numpy.repeat(numpy.arange(4), 4).reshape(4, 4)
Indexa = numpy.tile(numpy.arange(4), 4).reshape(4, 4)
numpy.transpose([numpy.compress(neighbours.flatten(), Indexa.flatten()),
numpy.compress(neighbours.flatten(), Indexb.flatten())])
array([[0, 1],
[1, 2],
[2, 3]])
答案 1 :(得分:1)
根据您的算法的一般概念,我认为您可以通过首先删除(或复制到另一个列表)仅2*abs(p.x - q.x) <= eps
的元素来减少经历欧氏距离测试的点列表重复y),这比计算所有点的欧几里得快得多。如果eps
很小,那就可以了。
答案 2 :(得分:1)
我不知道这是否会加速你的代码,但计算循环的pythonic方式是这样的:
for i, p in enumerate(points):
另外 - 我不确定每次都能理解搜索整个字典(地图)键的逻辑。这段代码看起来不像是在做一些有用的事情
neighBourMap[index] = q
这会将键的一对键值对:q,value:q添加到字典中。您是否尝试过使用列表,即
neighBourMap = []
答案 3 :(得分:1)
所有其他答案都是正确的,但它们不会给你带来巨大的加速。使用numpy数组会给你一点点加速,并行化会给你一个加速。但是如果你有100万个积分并且你仍然使用你当前的算法进行n ^ 2距离计算,那么加速就没有了。 (1百万)^ 2是很多人的方式。如果您使用numpy或不使用?
您应该切换算法。您应该将您的积分存储在k-d树中。这样,您可以将搜索集中在一些邻居候选人身上。您可以使用q
简单地迭代所有点q
,而不是遍历所有点|q.x - p.x| < eps and |q.y - p.y| < eps
。如果你的eps
很小并且每个点只有几个邻居,那么这应该会加速你的速度。
这是一个pdf,它描述了如何查找特定范围内所有点的算法:http://www.cse.unr.edu/~bebis/CS302/Handouts/kdtree.pdf
答案 4 :(得分:1)
你想要所有点的组合。您可以使用itertools.combinations
。
由于我们只制作了我们需要的组合,因此我们不需要继续查找字典索引来追加。我们可以将这一点及其邻居列表保持在一起。
将defaultdict
与list
一起使用意味着我们不必在第一次查找某个点时手动创建list
。
此外,您实际上并不想要欧几里德距离的值,您只想知道它是否小于其他值。因此,比较正方形会得到相同的结果。
要使用point
作为字典的键,它需要是不可变的,因此我们将其转换为元组:
def distance_squared(a, b):
diff = complex(*a) - complex(*b)
return diff.real ** 2 + diff.imag ** 2
from itertools import combinations
from collections import defaultdict
neighbours = defaultdict(list)
eps_squared = eps ** 2
point_neighbours = ((point, neighbours[tuple(point)]) for point in points)
for (p, p_neighbours), (q, _) in combinations(point_neighbours , r=2):
if distance_squared(p, q) <= eps_squared:
p_neighbours.append(q)
答案 5 :(得分:0)
首先,你可以替换
index in neighBourMap.keys()):`
只是
index in neighBourMap
由于不需要创建字典密钥的副本,因此运行得更快。
更好的是,使用defaultdict(list)
,无需在追加列表值之前检查密钥。