[英]Speeding up block of code in python
我發現了兩個潛在的原因,原因是給定的點是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}
關於如何提高代碼效率的任何建議?
這是一個平凡的並行問題的例子。
我的建議:
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]])
有了算法的總體思路,我認為您可以通過先刪除(或復制到另一個列表)僅2*abs(px - qx) <= eps
(重復y)的元素來減少經過歐幾里得距離測試的點的列表,這比計算所有點的歐幾里得要快得多。 如果eps
小,那將起作用。
我不知道這是否可以加快您的代碼的速度,但是計數循環的Python方法是這樣的:
for i, p in enumerate(points):
另外-我不確定我是否每次都能理解整個字典(map)鍵的邏輯。 這段代碼看起來並不像在做有用的事情
neighBourMap[index] = q
這會將鍵值對q的鍵值對添加到字典中。 您是否嘗試過僅使用列表,即
neighBourMap = []
所有其他答案都是正確的,但是它們不會給您帶來巨大的提速。 使用numpy數組可以使您加速,並行化可以使您加速。 但是,如果您擁有一百萬個點,並且仍然使用當前的算法(進行n ^ 2距離計算),那么加速將不夠。 (100萬)^ 2是通往許多目標的方法。 如果您使用numpy?
您應該切換算法。 您應該將點存儲在kd樹中。 這樣,您可以將搜索集中在幾個鄰近的候選對象上。 除了遍歷所有點q
,您可以簡單地使用|qx - px| < eps and |qy - py| < eps
遍歷所有點q
|qx - px| < eps and |qy - py| < eps
|qx - px| < eps and |qy - py| < eps
|qx - px| < eps and |qy - py| < eps
。 如果您的eps
很小,並且每個點只有幾個鄰居,那么這將使您的速度大大提高。
這是一份pdf文件,其中描述了該算法如何查找特定范圍內的所有點: http : //www.cse.unr.edu/~bebis/CS302/Handouts/kdtree.pdf
您希望所有點相互組合。 您可以使用itertools.combinations
。
由於我們僅在進行所需的組合,因此無需繼續查找要附加的字典索引。 我們可以將點及其鄰居列表放在一起。
對list
使用defaultdict
意味着我們不必在第一次查找點時手動創建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)
一件事情你可以取代
index in neighBourMap.keys()):`
只是
index in neighBourMap
由於不需要創建字典鍵的副本,因此運行速度更快。
更好的是,使用defaultdict(list)
可以避免在追加到列表值之前檢查鍵的需求。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.