python - 优化 Numpy 欧几里得距离和方向函数
问题描述
我正在尝试从 numpy 数组中的源坐标计算欧几里德距离和方向。
这是我能想到的,但是对于大型阵列来说它相对较慢。基于源坐标的欧几里得距离和方向严重依赖于每个像元的索引。这就是我循环每一行和每一列的原因。我研究了 scipy cdist、pdist 和 np linalg。
import numpy as np
from math import atan, degrees, sqrt
from timeit import default_timer
def euclidean_from_source(input_array, y_index, x_index):
# copy arrays
distance = np.empty_like(input_array, dtype=float)
direction = np.empty_like(input_array, dtype=int)
# loop each row
for i, row in enumerate(X):
# loop each cell
for c, cell in enumerate(row):
# get b
b = x_index - i
# get a
a = y_index - c
hypotenuse = sqrt(a * a + b * b) * 10
distance[i][c] = hypotenuse
direction[i][c] = get_angle(a, b)
return [distance, direction]
def calibrate_angle(a, b, angle):
if b > 0 and a > 0:
angle+=90
elif b < 0 and a < 0:
angle+=270
elif b > 0 > a:
angle+=270
elif a > 0 > b:
angle+=90
return angle
def get_angle(a, b):
# get angle
if b == 0 and a == 0:
angle = 0
elif b == 0 and a >= 0:
angle = 90
elif b == 0 and a < 0:
angle = 270
elif a == 0 and b >= 0:
angle = 180
elif a == 0 and b < 0:
angle = 360
else:
theta = atan(b / a)
angle = degrees(theta)
return calibrate_angle(a, b, angle)
if __name__ == "__main__":
dimension_1 = 5
dimension_2 = 5
X = np.random.rand(dimension_1, dimension_2)
y_index = int(dimension_1/2)
x_index = int(dimension_2/2)
start = default_timer()
distance, direction = euclidean_from_source(X, y_index, x_index)
print('Total Seconds {{'.format(default_timer() - start))
print(distance)
print(direction)
更新 我能够使用广播功能来做我需要的事情,而且速度只是其中的一小部分。但是我仍在研究如何在整个矩阵中将角度校准为 0、360(模数在这种情况下不起作用)。
import numpy as np
from math import atan, degrees, sqrt
from timeit import default_timer
def euclidean_from_source_update(input_array, y_index, x_index):
size = input_array.shape
center = (y_index, x_index)
x = np.arange(size[0])
y = np.arange(size[1])
# use broadcasting to get euclidean distance from source point
distance = np.multiply(np.sqrt((x - center[0]) ** 2 + (y[:, None] - center[1]) ** 2), 10)
# use broadcasting to get euclidean direction from source point
direction = np.rad2deg(np.arctan2((x - center[0]) , (y[:, None] - center[1])))
return [distance, direction]
def euclidean_from_source(input_array, y_index, x_index):
# copy arrays
distance = np.empty_like(input_array, dtype=float)
direction = np.empty_like(input_array, dtype=int)
# loop each row
for i, row in enumerate(X):
# loop each cell
for c, cell in enumerate(row):
# get b
b = x_index - i
# get a
a = y_index - c
hypotenuse = sqrt(a * a + b * b) * 10
distance[i][c] = hypotenuse
direction[i][c] = get_angle(a, b)
return [distance, direction]
def calibrate_angle(a, b, angle):
if b > 0 and a > 0:
angle+=90
elif b < 0 and a < 0:
angle+=270
elif b > 0 > a:
angle+=270
elif a > 0 > b:
angle+=90
return angle
def get_angle(a, b):
# get angle
if b == 0 and a == 0:
angle = 0
elif b == 0 and a >= 0:
angle = 90
elif b == 0 and a < 0:
angle = 270
elif a == 0 and b >= 0:
angle = 180
elif a == 0 and b < 0:
angle = 360
else:
theta = atan(b / a)
angle = degrees(theta)
return calibrate_angle(a, b, angle)
if __name__ == "__main__":
dimension_1 = 5
dimension_2 = 5
X = np.random.rand(dimension_1, dimension_2)
y_index = int(dimension_1/2)
x_index = int(dimension_2/2)
start = default_timer()
distance, direction = euclidean_from_source(X, y_index, x_index)
print('Total Seconds {}'.format(default_timer() - start))
start = default_timer()
distance2, direction2 = euclidean_from_source_update(X, y_index, x_index)
print('Total Seconds {}'.format(default_timer() - start))
print(distance)
print(distance2)
print(direction)
print(direction2)
更新2 感谢大家的回复,经过测试方法,这两种方法是最快的,并且产生了我需要的结果。我仍然对你们能想到的任何优化持开放态度。
def get_euclidean_direction(input_array, y_index, x_index):
rdist = np.arange(input_array.shape[0]).reshape(-1, 1) - x_index
cdist = np.arange(input_array.shape[1]).reshape(1, -1) - y_index
direction = np.mod(np.degrees(np.arctan2(rdist, cdist)), 270)
direction[y_index:, :x_index]+= -90
direction[y_index:, x_index:]+= 270
direction[y_index][x_index] = 0
return direction
def get_euclidean_distance(input_array, y_index, x_index):
size = input_array.shape
center = (y_index, x_index)
x = np.arange(size[0])
y = np.arange(size[1])
return np.multiply(np.sqrt((x - center[0]) ** 2 + (y[:, None] - center[1]) ** 2), 10)
解决方案
此操作非常容易矢量化。一方面,a
根本b
不需要在 2D 中计算,因为它们只依赖于数组中的一个方向。距离可以用 计算np.hypot
。广播会将形状转换为正确的 2D 形式。
您的角度函数几乎完全等同于np.degrees
应用于np.arctan2
。
目前尚不清楚为什么x
用标准的方式来标记行和列y
,但只要你保持一致,它应该没问题。
所以这里是矢量化版本:
def euclidean_from_source(input_array, c, r):
rdist = np.arange(input_array.shape[0]).reshape(-1, 1) - r
# Broadcasting doesn't require this second reshape
cdist = np.arange(input_array.shape[1]).reshape(1, -1) - c
distance = np.hypot(rdist, cdist) * 10
direction = np.degrees(np.arctan2(rdist, cdist))
return distance, direction
我将把它作为练习留给读者,以确定是否需要任何额外的处理来微调角度,如果需要,以矢量化的方式实现它。
推荐阅读
- android - 属性返回与嵌套 RecyclerView.Adapter 中的实际资源不同的颜色 ID
- java - 如何检查Java中的文本字段中是否输入了某个值
- python - 我想知道如何在 Pytorch 中处理 LSTM
- sql - SQL将列值作为枚举返回-来自另一列的GetValue()字段
- c# - 关于 ASP C# 应用程序和回发问题的 Crystal Report
- javascript - 为什么我必须使用方括号注释访问我的对象属性
- sql - 在已经过滤日期的视图上查询 trunc(date)
- java - SpringRest api 自动添加 {? (大括号)在 PathVariable 之后
- docker - asp net core docker serilog 配置
- linux - 为什么 add 或 imul 在 x86-64 中的嵌套循环中出错