加速python中的代码块

时间:2015-11-26 08:51:06

标签: python list dictionary scipy

我已经确定了以下代码片段执行效果非常糟糕的两个可能原因,因为points是10000个size-2列表的列表。

  1. "附加"给定一个键
  2. 添加新值
  3. 邻居地图字典。

    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}
    
  4. 关于如何提高代码效率的任何建议?

6 个答案:

答案 0 :(得分:2)

这是平凡并行问题的一个例子。

我的建议:

  • 使用numpy
  • 创建2个点^点矩阵(2D阵列),一个用于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

由于我们只制作了我们需要的组合,因此我们不需要继续查找字典索引来追加。我们可以将这一点及其邻居列表保持在一起。

defaultdictlist一起使用意味着我们不必在第一次查找某个点时手动创建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),无需在追加列表值之前检查密钥。