我有一个3D点的列表,我通过numpy.linalg.lstsq - 方法计算了一个平面。但现在我想对每个点进行正交投影到这个平面,但我找不到我的错误:
from numpy.linalg import lstsq
def VecProduct(vek1, vek2):
return (vek1[0]*vek2[0] + vek1[1]*vek2[1] + vek1[2]*vek2[2])
def CalcPlane(x, y, z):
# x, y and z are given in lists
n = len(x)
sum_x = sum_y = sum_z = sum_xx = sum_yy = sum_xy = sum_xz = sum_yz = 0
for i in range(n):
sum_x += x[i]
sum_y += y[i]
sum_z += z[i]
sum_xx += x[i]*x[i]
sum_yy += y[i]*y[i]
sum_xy += x[i]*y[i]
sum_xz += x[i]*z[i]
sum_yz += y[i]*z[i]
M = ([sum_xx, sum_xy, sum_x], [sum_xy, sum_yy, sum_y], [sum_x, sum_y, n])
b = (sum_xz, sum_yz, sum_z)
a,b,c = lstsq(M, b)[0]
'''
z = a*x + b*y + c
a*x = z - b*y - c
x = -(b/a)*y + (1/a)*z - c/a
'''
r0 = [-c/a,
0,
0]
u = [-b/a,
1,
0]
v = [1/a,
0,
1]
xn = []
yn = []
zn = []
# orthogonalize u and v with Gram-Schmidt to get u and w
uu = VecProduct(u, u)
vu = VecProduct(v, u)
fak0 = vu/uu
erg0 = [val*fak0 for val in u]
w = [v[0]-erg0[0],
v[1]-erg0[1],
v[2]-erg0[2]]
ww = VecProduct(w, w)
# P_new = ((x*u)/(u*u))*u + ((x*w)/(w*w))*w
for i in range(len(x)):
xu = VecProduct([x[i], y[i], z[i]], u)
xw = VecProduct([x[i], y[i], z[i]], w)
fak1 = xu/uu
fak2 = xw/ww
erg1 = [val*fak1 for val in u]
erg2 = [val*fak2 for val in w]
erg = [erg1[0]+erg2[0], erg1[1]+erg2[1], erg1[2]+erg2[2]]
erg[0] += r0[0]
xn.append(erg[0])
yn.append(erg[1])
zn.append(erg[2])
return (xn,yn,zn)
这会返回一个全部在平面上的点列表,但是当我显示它们时,它们不在它们应该的位置。 我相信已经有一种内置的方法来解决这个问题,但我找不到任何=(
答案 0 :(得分:11)
你对np.lstsq
的使用很差,因为你正在为它提供一个预先计算好的3x3矩阵,而不是让它完成这项工作。我会这样做:
import numpy as np
def calc_plane(x, y, z):
a = np.column_stack((x, y, np.ones_like(x)))
return np.linalg.lstsq(a, z)[0]
>>> x = np.random.rand(1000)
>>> y = np.random.rand(1000)
>>> z = 4*x + 5*y + 7 + np.random.rand(1000)*.1
>>> calc_plane(x, y, z)
array([ 3.99795126, 5.00233364, 7.05007326])
使用不依赖于z
的系数的平面公式实际上更方便,即使用a*x + b*y + c*z = 1
。您可以类似地计算a
,b
和c
:
def calc_plane_bis(x, y, z):
a = np.column_stack((x, y, z))
return np.linalg.lstsq(a, np.ones_like(x))[0]
>>> calc_plane_bis(x, y, z)
array([-0.56732299, -0.70949543, 0.14185393])
要使用我的替代方程将点投影到平面上,向量(a, b, c)
垂直于平面。很容易检查点(a, b, c) / (a**2+b**2+c**2)
是否在平面上,因此可以通过参考平面上该点的所有点,将点投影到法线向量上,从点中减去该投影来完成投影,然后将它们引回原点。你可以这样做:
def project_points(x, y, z, a, b, c):
"""
Projects the points with coordinates x, y, z onto the plane
defined by a*x + b*y + c*z = 1
"""
vector_norm = a*a + b*b + c*c
normal_vector = np.array([a, b, c]) / np.sqrt(vector_norm)
point_in_plane = np.array([a, b, c]) / vector_norm
points = np.column_stack((x, y, z))
points_from_point_in_plane = points - point_in_plane
proj_onto_normal_vector = np.dot(points_from_point_in_plane,
normal_vector)
proj_onto_plane = (points_from_point_in_plane -
proj_onto_normal_vector[:, None]*normal_vector)
return point_in_plane + proj_onto_plane
所以现在你可以这样做:
>>> project_points(x, y, z, *calc_plane_bis(x, y, z))
array([[ 0.13138012, 0.76009389, 11.37555123],
[ 0.71096929, 0.68711773, 13.32843506],
[ 0.14889398, 0.74404116, 11.36534936],
...,
[ 0.85975642, 0.4827624 , 12.90197969],
[ 0.48364383, 0.2963717 , 10.46636903],
[ 0.81596472, 0.45273681, 12.57679188]])
答案 1 :(得分:2)
你可以简单地完成矩阵中的所有操作。
如果您将点作为行向量添加到矩阵X
,而y
是向量,则最小二乘解的参数向量beta
为:
import numpy as np
beta = np.linalg.inv(X.T.dot(X)).dot(X.T.dot(y))
但是如果我们想做投影,有一种更简单的方法:QR分解为我们提供了一个正交投影矩阵,Q.T
,Q
本身就是标准正交基矢量的矩阵。因此,我们可以先形成QR
,然后获取beta
,然后使用Q.T
投射点数。
QR:
Q, R = np.linalg.qr(X)
测试:
# use R to solve for beta
# R is upper triangular, so can use triangular solver:
beta = scipy.solve_triangular(R, Q.T.dot(y))
现在我们有了beta
,我们可以非常简单地使用Q.T
投射点数:
X_proj = Q.T.dot(X)
多数民众赞成!
如果你想要更多的信息和图形图片和内容,我做了一大堆笔记,同时做了类似的事情,在https://github.com/hughperkins/selfstudy-IBP/blob/9dedfbb93f4320ac1bfef60db089ae0dba5e79f6/test_bases.ipynb
(编辑:请注意,如果你想添加一个偏见词,那么最合适的不必通过原点,你可以简单地将一个额外的列,包括全1,添加到X
,作为偏见词/特征)
答案 2 :(得分:0)
此网络page具有非常好的代码库。它很好地实现了Maple在# import numpy to perform operations on vector
import numpy as np
# vector u
u = np.array([2, 5, 8])
# vector n: n is orthogonal vector to Plane P
n = np.array([1, 1, 7])
# Task: Project vector u on Plane P
# finding norm of the vector n
n_norm = np.sqrt(sum(n**2))
# Apply the formula as mentioned above
# for projecting a vector onto the orthogonal vector n
# find dot product using np.dot()
proj_of_u_on_n = (np.dot(u, n)/n_norm**2)*n
# subtract proj_of_u_on_n from u:
# this is the projection of u on Plane P
print("Projection of Vector u on Plane P is: ", u - proj_of_u_on_n)
中阐述的theory,如下所示:
SELECT Vendor_Number, Vendor_Name FROM Vendors;