我有两个大点列表。一个包含表示矩形边缘的点 (edge_points)。Edge_points具有 XY 坐标。另一个列表包含矩形 (all_point) 内的点。All_point具有 XYZ 坐标。在第二个列表中,我想删除边缘周围点的xy"m"距离内的任何点(列表1)。
我有一个有效的代码,但它很慢,嵌套循环...我见过建议 cdist 的线程,但这不适用于我想将矩形中的每个点与单个边缘点进行比较的方案。Hypot 比使用 sqrt 更快,但仍然无法让我达到我想要的位置。
如何提高此代码的效率?
all_point=colpoint+rowpoint
all_points=[]
for n in range(0,len(all_point)):
#Calculate xy distance between inflection point and edge points
test_point=all_point[n]
dist=[]
for k in range(0,len(edge_points)):
test_edge=edge_points[k]
dist_edge=math.hypot((test_point[1]-test_edge[1])+(test_point[0]-test_edge[0]))
dist.append(dist_edge)
if all(i >= m for i in dist) is True:
all_points.append(test_point)
else:
continue
Vectorise,vectorise也适用于这里:
import numpy as np
all_point, edge_points = np.asanyarray(all_point), np.asanyarray(edge_points)
squared_dists = ((all_point[:, None, :2] - edge_points[None, :, :])**2).sum(axis=-1)
mask = np.all(squared_dists > m**2, axis=-1)
all_points = all_point[mask, :]
请注意,在 Python 级别没有更多的循环。矢量化将这些循环移动到编译的代码,其执行速度提高了几倍。
具体来说,我们创建了两个数组all_point
大小N1x2
和大小edge_points
N2x2
.然后将它们重新塑造为N1x1x2
和1xN2x2
的大小(通过索引中的None
秒)。
当取差值时,这会触发前两个轴的广播,使得生成的数组具有形状N1xN2x2
,并包含all_point
和edge_points
坐标之间的所有成对差异。随后的平方一次性应用于所有N1xN2x2
元素。由axis
参数指定的总和沿最后一个轴取,即超过x
和y
以产生一个N1xN2
的平方成对距离数组。
接下来的几行演示了if
语句的矢量化等效项。为了能够一次性执行它们,人们会创建真相面具。与m**2
的比较是按元素完成的,因此我们得到了squared_dists
的每个N1xN2
元素的真值。np.all
类似于Pythonall
,但可以同时执行多个组。这由axis
参数控制。在这里,它指定应按行应用all
,以产生N1
真值。
此掩码在形状上与all_point
匹配,可用于提取满足条件的所有坐标对。
总结广播允许从Python级别消除嵌套循环并将其替换为一些矢量化操作。只要内存不是问题,就会带来巨大的加速。
如果内存是一个问题:这是一个使用tensordot
的节省内存的变体
all_point, edge_points = np.asanyarray(all_point), np.asanyarray(edge_points)
mixed = np.tensordot(all_point[:, :2], -2 * edge_points, (1, 1))
mixed += (all_point[:, :2]**2).sum(axis=-1)[:, None]
mixed += (edge_points**2).sum(axis=-1)[None, :]
mask = np.all(mixed > m**2, axis=-1)
all_points = all_point[mask, :]
如果它仍然太大,我们将不得不将all_point
或edge_points
切成可管理的部分。
all_point, edge_points = np.asanyarray(all_point), np.asanyarray(edge_points)
mask = np.ones((len(all_point),), dtype=bool)
mm = m*m - (all_point[:,:2]**2).sum(axis=-1)[:, None]
chunksize = <choose according to your memory and data size>
for i in range(0, len(edge_points), chunksize):
mixed = np.tensordot(all_point[mask, :2], -2 * edge_points[i:i+chunksize, :], (1, 1))
mixed += (edge_points[None, i:i+chunksize, :]**2).sum(axis=-1)
mask[mask] &= np.all(mixed > mm[mask], axis=-1)
all_points = all_point[mask, :]