Python 在for循环中重写阵列的速度性能
我有一个带有Python 在for循环中重写阵列的速度性能,python,performance,numpy,optimization,Python,Performance,Numpy,Optimization,我有一个带有shape=(500500)的2D数据集。从给定位置(x\u 0,y\u 0)我想将每个元素/像素的距离映射到该给定位置。我通过确定从(x\u 0,y\u 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.41
shape=(500500)
的2D数据集。从给定位置(x\u 0,y\u 0)
我想将每个元素/像素的距离映射到该给定位置。我通过确定从(x\u 0,y\u 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
循环是一个瓶颈。对于我需要的应用程序来说,它花费的时间太长。有没有办法加速/提高其性能?或者是否有一种完全不同但更快的方法来获得我需要的输出
根的整个适用切片
,从参考点映射到矩阵的边缘numpy
的矢量化操作为您完成它,例如
dist = np.sqrt(dist_matrix)
根的整个适用切片
,从参考点映射到矩阵的边缘numpy
的矢量化操作为您完成它,例如
dist = np.sqrt(dist_matrix)
这是一种使用
掩蔽的矢量化方法
-
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
标杆管理
使用数据形状(500500)
,使用问题样本中使用的数字范围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
甚至1000
的情况,扩展不会对这些数字的叠加方式产生太大的变化-
In [397]: np.random.seed(0)
...: data = np.random.randint(0,100,(500,500))
...: x_0 = 50
...: y_0 = 50
In [398]: %timeit func(data, (x_0,y_0))
...: %timeit func_return_inverse(data, (x_0,y_0))
...: %timeit func_mask_vectorized(data, (x_0,y_0))
1 loop, best of 3: 5.62 s per loop
10 loops, best of 3: 20.7 ms per loop
100 loops, best of 3: 4.28 ms per loop
In [399]: np.random.seed(0)
...: data = np.random.randint(0,1000,(500,500))
...: x_0 = 500
...: y_0 = 500
In [400]: %timeit func(data, (x_0,y_0))
...: %timeit func_return_inverse(data, (x_0,y_0))
...: %timeit func_mask_vectorized(data, (x_0,y_0))
1 loop, best of 3: 6.87 s per loop
10 loops, best of 3: 21.9 ms per loop
100 loops, best of 3: 5.05 ms per loop
这是一种使用
掩蔽的矢量化方法
-
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
标杆管理
使用数据形状(500500)
,使用问题样本中使用的数字范围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
甚至1000
的情况,扩展不会对这些数字的叠加方式产生太大的变化-
In [397]: np.random.seed(0)
...: data = np.random.randint(0,100,(500,500))
...: x_0 = 50
...: y_0 = 50
In [398]: %timeit func(data, (x_0,y_0))
...: %timeit func_return_inverse(data, (x_0,y_0))
...: %timeit func_mask_vectorized(data, (x_0,y_0))
1 loop, best of 3: 5.62 s per loop
10 loops, best of 3: 20.7 ms per loop
100 loops, best of 3: 4.28 ms per loop
In [399]: np.random.seed(0)
...: data = np.random.randint(0,1000,(500,500))
...: x_0 = 500
...: y_0 = 500
In [400]: %timeit func(data, (x_0,y_0))
...: %timeit func_return_inverse(data, (x_0,y_0))
...: %timeit func_mask_vectorized(data, (x_0,y_0))
1 loop, best of 3: 6.87 s per loop
10 loops, best of 3: 21.9 ms per loop
100 loops, best of 3: 5.05 ms per loop
使用
unique
的return\u inverse
-参数:
def func(data, (x_0,y_0)):
y, x = numpy.indices(data.shape)
r = (x - x_0)**2 + (y - y_0)**2
float_values, r = numpy.unique(r, return_inverse=True)
return float_values ** 0.5, r.reshape(data.shape)
使用
unique
的return\u inverse
-参数:
def func(data, (x_0,y_0)):
y, x = numpy.indices(data.shape)
r = (x - x_0)**2 + (y - y_0)**2
float_values, r = numpy.unique(r, return_inverse=True)
return float_values ** 0.5, r.reshape(data.shape)
您的索引方案(数据中的整数)的顺序与距离相同。如果总是这样,则可以生成距离数组,而不需要数据的实际内容 我将这个解决方案建立在索引计算的基础上,它使用每个位置到锚定位置的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)确定的水平和垂直detla范围
对于距离计算,我们可以忽略垂直/水平方向,创建一个小范围和一个大范围(r)。(来自maxD和minD的lo和hi)
为了计算所有的平方和,我们可以将两个范围中的一个转换为垂直向量(lo),然后将它们的值(hi*hi+lo*lo)平方后将其添加到另一个(hi)。这将生成一个包含所有平方和(SQ)组合的2D矩阵
在这个矩阵中,顶三角形是其对应物的复制品。因此,我们用三角形布尔矩阵来掩盖重复的距离对。(唯一)屏蔽顶部三角形将确保屏蔽操作产生的平方和顺序正确
最后,过滤后的sqs值正好包含我们需要的内容,并且顺序正确。我们只能对最终结果应用代价高昂的平方根函数
不将距离计算应用于每个像素应该会带来一些显著的性能提升,因为这将允许您仅在需要时使用索引距离。我想将此distanceArray函数的性能与其他解决方案进行比较是不公平的(因为它只做了它们所做的一部分),但是,考虑到不必做某些事情也是优化的一部分,最终结果可能会更好(在我的非科学测试中,大约是Divakar的5倍)
请注意,如果仅对一小部分像素使用距离,则可能希望避免所有这些计算,并使用字典作为缓存,根据dX和dY偏移量(键控和有序元组)“按需”计算距离。这将执行绝对最小数量的计算,并仅为任何特定偏移对计算一次距离。您甚至可以继续将该缓存用于其他锚点位置和数据形状,因为无论锚点的位置如何,偏移对都将始终产生相同的距离
[编辑]要获得与我用于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]])
您的索引方案(在