对数组进行高效的双重迭代

时间:2014-08-17 09:10:05

标签: python arrays numpy

我有以下代码,其中points是多行3列cols列表,coorRadius是我想要找到局部坐标最大值的半径,localCoordinateMaxima是一个数组,我存储这些最大值的i:

for i,x in enumerate(points):
        check = 1
        for j,y in enumerate(points):
            if linalg.norm(x-y) <= coorRadius and x[2] < y[2]:
                check = 0

        if check == 1:
            localCoordinateMaxima.append(i)

    print localCoordinateMaxima

不幸的是,当我有几千点时,这需要永远,我正在寻找一种加速它的方法。我尝试用all()条件来做,但是我没有管理它,我甚至不确定它会更有效率。你们能提出一种让它更快的方法吗?

最佳!

3 个答案:

答案 0 :(得分:2)

以下是您的代码版本,只是收紧了一点:

for i, x in enumerate(points):
    x2 = x[2]
    for y in points:
        if linalg.norm(x-y) <= coorRadius and x2 < y[2]:
            break
    else:
        localCoordinateMaxima.append(i)
    print localCoordinateMaxima

的变化:

  • x[2]查询分解出来。
  • j 变量未使用。
  • 为早期
  • 添加中断
  • 使用for-else结构而不是标志变量

答案 1 :(得分:2)

您最好使用KDTree搜索邻居。

from scipy.spatial import cKDTree

tree = cKDTree(points)
pairs = tree.query_pairs(coorRadius)

现在pairs是一组两个项目元组(i, j),其中i < jpoints[i]以及points[j]coorRadius之内。您现在可以简单地迭代这些,这可能比您当前迭代的len(points)**2小得多:

is_maximum = [True] * len(points)
for i, j in pairs:
    if points[i][2] < points[j][2]:
        is_maximum[i] = False
    elif points[j][2] < points[i][2]:
        is_maximum[j] = False
localCoordinateMaxima, = np.nonzero(is_maximum)

这可以通过矢量化来加速:

pairs = np.array(list(pairs))
pairs = np.vstack((pairs, pairs[:, ::-1]))
pairs = pairs[np.argsort(pairs[:, 0])]
is_z_smaller = points[pairs[:, 0], 2] < points[pairs[:, 1], 2]
bins, = np.nonzero(pairs[:-1, 0] != pairs[1:, 0])
bins = np.concatenate(([0], bins+1))
is_maximum = np.logical_and.reduceat(is_z_smaller, bins)
localCoordinateMaxima, = np.nonzero(is_maximum)

上述代码假设每个点在coorRadius内至少有一个邻居。如果不是这样,你需要稍微复杂一点:

pairs = np.array(list(pairs))
pairs = np.vstack((pairs, pairs[:, ::-1]))
pairs = pairs[np.argsort(pairs[:, 0])]
is_z_smaller = points[pairs[:, 0], 2] < points[pairs[:, 1], 2]
bins, = np.nonzero(pairs[:-1, 0] != pairs[1:, 0])
has_neighbors = pairs[np.concatenate(([True], bins)), 0]
bins = np.concatenate(([0], bins+1))
is_maximum = np.ones((len(points),), bool)
is_maximum[has_neighbors] &= np.logical_and.reduceat(is_z_smaller, bins)
localCoordinateMaxima, = np.nonzero(is_maximum)

答案 2 :(得分:1)

numpy这不太难。如果需要,可以使用单个(长)表达式执行此操作:

import numpy as np

points = np.array(points)
localCoordinateMaxima = np.where(np.all((np.linalg.norm(points-points[None,:], axis=-1) >
                                         coorRadius) |
                                        (points[:,2] >= points[:,None,2]),
                                        axis=-1))

您当前代码实现的算法基本上是where(not(any(w <= x and y < z)))。如果你通过其内部的逻辑运算(使用Demorgan的定律)分发not,你可以通过翻转不等式来避免一级嵌套,获得where(all(w > x or y >= z)))

w是应用于一起广播的点的差异的规范矩阵。 x是一个常量。 yz都是具有点的第三个坐标的数组,其形状使得它们一起广播为与w相同的形状。