【发布时间】:2022-02-02 08:31:57
【问题描述】:
我有3个数组(x_array, y_array, p_array),前两个对应2d数组,坐标点是随机点,第三个是扁平点数组,对应线。
我需要计算每个 x_array、y_array 点到 p_array 点形成的线的最小正交距离。
为此,我编写了两个主要使用numpy 的函数。第一个函数创建一个布尔掩码来获取哪些点与线有正交投影,这会产生一个具有 True 和 False 值的二维数组。第二个函数计算到直线的正交距离或与点相交的正交投影。
此过程有效,但它是 GUI 的一部分,此过程对时间敏感。
我想加快这个功能,但我对numpy 的一般编程知识不足以改善这一点。我已经使用numba 编写了此代码,但第一个函数的时间从 1.68 秒小幅减少到 1.09 秒。
目标
我的目标是将两个函数的时间都减少到1s以下,我不知道这是否可能。 我会很感激任何帮助
结果
mask: 1.6823785305023193
mask numba: 1.0962464809417725
orto distance: 1.630366563796997
代码
数据:
https://drive.google.com/file/d/1GHDSH1ycQSiBJk8RvkTNiE_RoetwCZw6/view?usp=sharing https://drive.google.com/file/d/1k8wCop1fePEh7ANfkScQ_BHLGRVo-aJe/view?usp=sharing https://drive.google.com/file/d/1swhg1BXBh18xyuueOKUNNx3SVGW8Jfq9/view?usp=sharing
import numpy as np
import time
def ortoSegmentPoint_all(p_array, x_array, y_array):
"""
:param p_array: np.array([[ 718898.941 9677612.901 ], [ 718888.8227 9677718.305 ], [ 719033.0528 9677770.692 ]])
:param y_array: np.array([9677656.39934991 9677720.27550726 9677754.79])
:param x_array: np.array([718895.88881594 718938.61392781 718961.46])
:return: [POINT, LINE] indexes where point is orthogonal to line segment
"""
# PENDIENTE "m" de la recta, y = mx + n
m_array = np.divide(y_array[:, 0] - y_array[:, 1], x_array[:, 0] - x_array[:, 1])
# PENDIENTE INVERTIDA, 1/m
inv_m_array = np.divide(1, m_array)
# VALOR "n", y = mx + n
n_array = y_array[:, 0] - x_array[:, 0] * m_array
# VALOR "n_orto" PARA LA RECTA PERPENDICULAR
_len_p_array = len(p_array)
n_orto_array = np.reshape(p_array[:, 1], (_len_p_array, 1)) + inv_m_array * np.reshape(p_array[:, 0], (_len_p_array, 1))
# PUNTOS DONDE SE INTERSECTAN DE FORMA PERPENDICULAR
x_intersec_array = np.divide(n_orto_array - n_array, m_array + inv_m_array)
y_intersec_array = m_array * x_intersec_array + n_array
# LISTAR COORDENADAS EN PARES
# FILAS: NUMERO DE PUNTOS, COLUMNAS: NUMERO DE TRAMOS
maskX = np.where(np.logical_and(x_intersec_array < np.max(x_array, axis=1), x_intersec_array > np.min(x_array, axis=1)), True, False)
maskY = np.where(np.logical_and(y_intersec_array < np.max(y_array, axis=1), y_intersec_array > np.min(y_array, axis=1)), True, False)
mask = maskY * maskX
return mask
def ortoDistancePointLine_indx(x_array, y_array, p_array, index_point, mask):
' a=(yA−yB), b=(xB−xA) and c=xAyB−xByA '
a, b = np.array([x_array[:, 0], y_array[:, 0]]), np.array([x_array[:, 1], y_array[:, 1]])
a0, b0, c0 = a[1, :] - b[1, :], b[0, :] - a[0, :], a[0, :] * b[1, :] - b[0, :] * a[1, :]
index_point = np.unique(index_point)
_len = len(p_array[index_point])
_x = p_array[index_point][:, 0].reshape(_len, 1)
_y = p_array[index_point][:, 1].reshape(_len, 1)
_a0, _b0, _c0 = np.full(shape=(_len, a0.size), fill_value=a0), np.full(shape=(_len, b0.size), fill_value=b0), np.full(shape=(_len, c0.size), fill_value=c0)
_d0 = np.abs(_a0 * _x + _b0 * _y + _c0)
_d1 = np.sqrt((_a0 * _a0) + (_b0 * _b0))
_d = np.divide(_d0, _d1) * mask[index_point, :]
_d = np.where(_d == 0, 1000000.0, _d)
return _d, index_point
p_array = np.load('p_array.npy')
x_array = np.load('x_array.npy')
y_array = np.load('y_array.npy')
#CREATE BOOLEAN MASK
t0 = time.time()
mask = ortoSegmentPoint_all(p_array, x_array, y_array)
print('mask: ', time.time() - t0)
# ORTHOGONAL DISTANCE
rows, cols = np.where(mask)
t0 = time.time()
dist_orto, idx_orto = ortoDistancePointLine_indx(x_array, y_array, p_array, rows, mask)
print('orto distance: ', time.time() - t0)
【问题讨论】:
-
只是一些可能的事情来提高速度:1-避免使用
np.where,它很慢并且总是可以用布尔掩码代替,例如_d = np.where(_d == 0, 1E6, _d)可以变成_d[_d == 0] = 1E6; 2- 将_a0 = np.full(...)更改为_a0 = np.copy(a0)。如果没有那么多相等的值,也有可能不做index_point = np.unique(index_point)会更快。如果numpy不起作用,您最终可以尝试cython。 -
开发高性能 Numba 代码的第一部分通常是编写由循环组成的代码(避免矢量化命令,有一些例外),避免列表等)。如果高效的 numpy 代码是目标,您通常会尝试其他方式(避免循环、矢量化命令)