for循环

时间:2018-04-17 19:59:12

标签: python performance numpy optimization

我有一个带shape = (500, 500)的2D数据集。从给定位置(x_0, y_0)我想将每个元素/像素的距离映射到给定位置。我这样做是通过确定(x_0, y_0)的所有唯一距离并使用整数映射它们。这样的6 x 6数据集的映射如下所示:

[9 8 7 6 7 8]
[8 5 4 3 4 5]
[7 4 2 1 2 4]
[6 3 1 0 1 3]
[7 4 2 1 2 4]
[8 5 4 3 4 5]

其中整数对应于存储在以下数组中的唯一距离:

[0.  1.  1.41421356  2.  2.23606798  2.82842712  3.  3.16227766  3.60555128  4.24264069]

确定这些距离的代码如下:

def func(data, (x_0,y_0)):
  y, x = numpy.indices((data.shape))
  r = numpy.sqrt((x - x_0)**2 + (y - y_0)**2)

  float_values = numpy.unique(r.ravel())  # Unique already sorts the result 
  int_values = numpy.arange(float_values.shape[0]).astype(numpy.int) 

  for idx in range(float_values.shape[0])[::-1]:
    r[r == float_values[idx]] = int_values[idx] 

  return float_values, r

for循环是一个瓶颈。我需要的应用程序需要很长时间。有没有办法加快/提高其性能?或者是否有一种完全不同但更快的方法来获得我需要的输出?

4 个答案:

答案 0 :(得分:2)

这是一个利用roll a 20 sided dice -

的矢量化方法
{
  "name": "rollDice",
  "confirmationStatus": "NONE",
  "slots": {
    "sides": {
      "name": "sides",
      "confirmationStatus": "NONE"
    }
  }
}

基准

使用形状masking的数据对建议的设置进行定时,使用def func_mask_vectorized(data, (x_0, y_0)): # Leverage broadcasting with open meshes to create the squared distances/ids m,n = data.shape Y,X = np.ogrid[:m,:n] ids = (X-x_0)**2 + (Y-y_0)**2 # Setup mask that will help us retrieve the unique "compressed" IDs # (similar to what return_inverse does). # This is done by setting 1s at ids places and then using that mask to # assign range covered array, in effect setting up the unique compress. IDs. mask = np.zeros(ids.max()+1, dtype=bool) mask[ids] = 1 id_arr = mask.astype(int) id_arr[mask] = np.arange(mask.sum()) r_out = id_arr[ids] # Finally extract out the unique ones among the IDs & get their sqrt values float_values_out = np.sqrt(np.flatnonzero(mask)) return float_values_out, r_out 的数字范围,同样在问题的样本中使用,并计算下面这一部分中的所有完整解决方案 - < / p>

(500,500)

对于数字可能会扩展到0-9甚至In [371]: np.random.seed(0) ...: data = np.random.randint(0,10,(500,500)) ...: x_0 = 2 ...: y_0 = 3 # Original soln In [372]: %timeit func(data, (x_0,y_0)) 1 loop, best of 3: 6.77 s per loop # @Daniel's soln In [373]: %timeit func_return_inverse(data, (x_0,y_0)) 10 loops, best of 3: 23.9 ms per loop # Soln from this post In [374]: %timeit func_mask_vectorized(data, (x_0,y_0)) 100 loops, best of 3: 5.02 ms per loop 的情况进行扩展并不会对这些数据的累积方式产生太大影响 -

100

答案 1 :(得分:1)

  1. 不要乱用你的“独特距离”阵列。只需预先计算距离,由radicand(平方和)索引。这只是

    roots = [sqrt(float(i))for i in range(upper_limit)]

  2. 然后,由于像素是连续的,您可以选择从参考点向外循环,只需将整个适用的roots切片从参考点映射到矩阵的边缘。

  3. 或者,完全退出循环:让numpy的向量化操作为你做,例如

    dist = np.sqrt(dist_matrix)
    

答案 2 :(得分:1)

使用return_inverse的{​​{1}} - 参数:

unique

答案 3 :(得分:0)

它表示您的索引方案(数据中的整数)与距离的顺序相同。如果总是如此,则可以在没有数据的实际内容的情况下生成距离数组。

我将此解决方案基于索引计算,该计算使用每个位置的x和y像素偏移到锚位置。假设“so”是最小偏移量,“ho”是较大的偏移量,“mo”是任一方向上的最大可能偏移量:

指数= ho +(mo + 1)* lo - lo *(lo + 1)// 2

要计算数组中的距离,我们只需要知道矩阵的尺寸和锚像素的位置。

import numpy as np
def distanceArray(x,y,cols,rows):
    maxDx  = max(x,cols-x)
    maxDy  = max(y,rows-y)
    maxD   = max(maxDx,maxDy)
    minD   = min(maxDx,maxDy)
    lo = np.arange(minD)[:,None]
    hi = np.arange(maxD)
    sqs = lo*lo + hi*hi
    unique = np.tri(*sqs.shape,maxD-minD, dtype=bool)[::-1,::-1]
    return np.sqrt(sqs[unique])

如果我们只关注锚点位置的像素偏移,我们将得到一系列水平和垂直德拉特,它们由数据的形状边界(maxDx和maxDy)决定。

对于距离计算,我们可以忽略垂直/水平方向并创建一个小的和大的(r)范围。 (lo和hi来自maxD和minD)

要计算所有平方和,我们可以将两个范围中的一个转换为垂直向量(lo),然后在平方它们的值后将其添加到另一个(hi)(hi * hi + lo * lo) 。这产生了具有平方和(sqs)的所有组合的2D矩阵。

在该矩阵之外,顶部三角形是其对应物的重复。因此,我们使用三角形布尔矩阵屏蔽重复的距离对。 (唯一)屏蔽顶部三角形将确保从屏蔽操作中出现的平方和的顺序是合适的顺序。

最后,过滤的sqs值恰好包含了我们需要的内容,并且顺序正确。我们可以将昂贵的平方根函数应用于最终结果。

不对每个像素应用距离计算应该可以获得一些显着的性能提升,因为它只允许您在需要时使用索引距离。我想将distanceArray函数的性能与其他解决方案的性能进行比较是不公平的(因为它只是它们所做的部分),但是,考虑到不必做某事也是优化的一部分,最终结果可能是更好(比我在非科学测试中的Divakar的5倍)。

请注意,如果您仅使用一小部分像素的距离,则可能需要避免所有这些计算,并使用字典作为缓存来根据dX和dY偏移“按需”计算距离(键入并命令元组)。这将执行绝对最小数量的计算,并且仅计算任何特定偏移对的距离一次。您甚至可以继续将该缓存用于其他锚位置和数据形状,因为无论锚的位置如何,偏移对都将始终产生相同的距离。

[EDIT]获取与我用于distanceArray相同的索引,你可以使用这个:

def offsets(x,y,cols,rows):
    mo   = max(x,cols-x-1,y,rows-y-1)+1

    dx   = abs(np.arange(cols)-x)
    dy   = abs(np.arange(rows)-y)[:,None]

    mo21 = 2 * mo - 1
    ly = dy*(mo21 - dy )//2  # mo*lo - lo*(lo+1)//2 when dy is lowest
    lx = dx*(mo21 - dx )//2  # mo*lo - lo*(lo+1)//2 when dx is lowest

    return np.maximum(dx,dy) + np.minimum(lx,ly)

offsets(3,3,6,6)

array([[9, 8, 6, 3, 6, 8],
       [8, 7, 5, 2, 5, 7],
       [6, 5, 4, 1, 4, 5],
       [3, 2, 1, 0, 1, 2],
       [6, 5, 4, 1, 4, 5],
       [8, 7, 5, 2, 5, 7]])