具有不同坐标原点的坐标向量的基础的编程更改(python/general 数学)
Programmatical Change of basis for coordinate vectors with different origin of coordinates (python/general maths)
我有一个支持向量机,它使用决策超平面将我的数据一分为二(出于可视化目的,这是一个具有三个维度的示例数据集),如下所示:
现在我想改变基础,使超平面平躺在 x/y 平面上,这样从每个样本点到决策超平面的距离就是它们的 z 坐标。
为此,我知道我需要更改基础。 SVM 的超平面由它们的系数(3d 向量)和截距(标量)给出,使用(据我所知)数学平面的一般形式:ax+by+cz=d
,其中 a、b、c 为系数的坐标和 d 是截距。当绘制为 3d-Vector 时,系数是垂直于平面的矢量(在图像中是青色线)。
现在 change of basis:如果没有截距,我可以假设作为系数的向量是我新基的一个向量,另一个可以是平面上的随机向量第三个是两者的简单叉积,得到三个正交向量,可以作为变换矩阵的列向量。
下面代码中使用的 z 函数来自平面一般形式的简单术语重排:ax+by+cz=d <=> z=(d-ax-by)/c
:
z_func = lambda interc, coef, x, y: (interc-coef[0]*x -coef[1]*y) / coef[2]
def generate_trafo_matrices(coefficient, z_func):
normalize = lambda vec: vec/np.linalg.norm(vec)
uvec2 = normalize(np.array([1, 0, z_func(1, 0)]))
uvec3 = normalize(np.cross(uvec1, uvec2))
back_trafo_matrix = np.array([uvec2, uvec3, coefficient]).T
#in other order such that its on the xy-plane instead of the yz-plane
trafo_matrix = np.linalg.inv(back_trafo_matrix)
return trafo_matrix, back_trafo_matrix
然后将此转换矩阵应用于所有点,如下所示:
def _transform(self, points, inverse=False):
trafo_mat = self.inverse_trafo_mat if inverse else self.trafo_mat
points = np.array([trafo_mat.dot(point) for point in points])
return points
现在,如果截距为零,那将完美运行,并且飞机将在 xy 轴上保持平坦。然而,一旦我有截距!=零,飞机就不再平坦了:
我理解是这样,因为这不是简单的基础变化,因为我的另一个基础的坐标原点不在(0,0,0)而是在不同的地方(超平面可能是在任何时候穿过系数向量),但我尝试将截距添加到转换中都没有得到正确的结果:
def _transform(self, points, inverse=False):
trafo_mat = self.inverse_trafo_mat if inverse else self.trafo_mat
intercept = self.intercept if inverse else -self.intercept
ursprung_translate = trafo_mat.dot(np.array([0,0,0])+trafo_matrix[:,0]*intercept)
points = np.array([point+trafo_matrix[:,0]*intercept for point in points])
points = np.array([trafo_mat.dot(point) for point in points])
points = np.array([point-ursprung_translate for point in points])
return points
例如是错误的。我在 Whosebug 上而不是在数学 StackExchange 上问这个问题,因为我认为我无法将相应的数学转换成代码,我很高兴我能做到这一点。
我已经创建了一个 github 要点,其中包含用于执行转换的代码并在 https://gist.github.com/cstenkamp/0fce4d662beb9e07f0878744c7214995, which can be launched using Binder under the link https://mybinder.org/v2/gist/jtpio/0fce4d662beb9e07f0878744c7214995/master?urlpath=lab%2Ftree%2Fchange_of_basis_with_translate.ipynb 处创建绘图,如果有人想使用代码本身的话。
感谢任何帮助!
这里的问题是你的平面是仿射space,不是向量space,所以你不能使用通常的变换矩阵公式。
仿射 space 中的坐标系由原点和基(放在一起称为 仿射框架 )给出。比如你的原点是O,那么M点在仿射坐标系中的坐标就是OM向量在仿射坐标系中的坐标。
如您所见,“正常”R^3 space 是仿射 space 的特例,其中原点为 (0,0,0)。
一旦我们确定了这些,我们就可以在仿射 spaces 中使用框架变化公式:如果我们有两个仿射框架 R = (O, b)
和 R' = (O', b')
,则基础变化公式对于点 M 是:M(R') = base_change_matrix_from_b'_to_b * (M(R) - O'(R))
(O'(R)
O' 在 R 定义的坐标系中的坐标)。
在我们的例子中,我们尝试从原点为 (0,0,0) 的帧出发,然后
规范基础,到一个框架,其中原点是 (0,0,0) 在平面上的正交投影,并且基础是,例如,在您的初始 post.
中描述的那个
让我们执行这些步骤:
首先,我们将定义一个 Plane
class 让我们的生活更轻松一些:
from dataclasses import dataclass
import numpy as np
@dataclass
class Plane:
a: float
b: float
c: float
d: float
@property
def normal(self):
return np.array([self.a, self.b, self.c])
def __contains__(self, point:np.array):
return np.isclose(self.a*point[0] + self.b*point[1] + self.c*point[2] + self.d, 0)
def project(self, point):
x,y,z = point
k = (self.a*x + self.b*y + self.c*z + self.d)/(self.a**2 + self.b**2 + self.c**2)
return np.array([x - k*self.a, y-k*self.b, z-k*self.c])
def z(self, x, y):
return (- self.d - self.b*y - self.a*x)/self.c
然后我们可以实现 make_base_changer
,它将 Plane
作为输入,return 2 个 lambda 函数执行正向和反向变换(采用和 return一个点)。你应该可以测试
def normalize(vec):
return vec/np.linalg.norm(vec)
def make_base_changer(plane):
uvec1 = plane.normal
uvec2 = [0, -plane.d/plane.b, plane.d/plane.c]
uvec3 = np.cross(uvec1, uvec2)
transition_matrix = np.linalg.inv(np.array([uvec1, uvec2, uvec3]).T)
origin = np.array([0,0,0])
new_origin = plane.project(origin)
forward = lambda point: transition_matrix.dot(point - new_origin)
backward = lambda point: np.linalg.inv(transition_matrix).dot(point) + new_origin
return forward, backward
我有一个支持向量机,它使用决策超平面将我的数据一分为二(出于可视化目的,这是一个具有三个维度的示例数据集),如下所示:
为此,我知道我需要更改基础。 SVM 的超平面由它们的系数(3d 向量)和截距(标量)给出,使用(据我所知)数学平面的一般形式:ax+by+cz=d
,其中 a、b、c 为系数的坐标和 d 是截距。当绘制为 3d-Vector 时,系数是垂直于平面的矢量(在图像中是青色线)。
现在 change of basis:如果没有截距,我可以假设作为系数的向量是我新基的一个向量,另一个可以是平面上的随机向量第三个是两者的简单叉积,得到三个正交向量,可以作为变换矩阵的列向量。
下面代码中使用的 z 函数来自平面一般形式的简单术语重排:ax+by+cz=d <=> z=(d-ax-by)/c
:
z_func = lambda interc, coef, x, y: (interc-coef[0]*x -coef[1]*y) / coef[2]
def generate_trafo_matrices(coefficient, z_func):
normalize = lambda vec: vec/np.linalg.norm(vec)
uvec2 = normalize(np.array([1, 0, z_func(1, 0)]))
uvec3 = normalize(np.cross(uvec1, uvec2))
back_trafo_matrix = np.array([uvec2, uvec3, coefficient]).T
#in other order such that its on the xy-plane instead of the yz-plane
trafo_matrix = np.linalg.inv(back_trafo_matrix)
return trafo_matrix, back_trafo_matrix
然后将此转换矩阵应用于所有点,如下所示:
def _transform(self, points, inverse=False):
trafo_mat = self.inverse_trafo_mat if inverse else self.trafo_mat
points = np.array([trafo_mat.dot(point) for point in points])
return points
现在,如果截距为零,那将完美运行,并且飞机将在 xy 轴上保持平坦。然而,一旦我有截距!=零,飞机就不再平坦了:
我理解是这样,因为这不是简单的基础变化,因为我的另一个基础的坐标原点不在(0,0,0)而是在不同的地方(超平面可能是在任何时候穿过系数向量),但我尝试将截距添加到转换中都没有得到正确的结果:
def _transform(self, points, inverse=False):
trafo_mat = self.inverse_trafo_mat if inverse else self.trafo_mat
intercept = self.intercept if inverse else -self.intercept
ursprung_translate = trafo_mat.dot(np.array([0,0,0])+trafo_matrix[:,0]*intercept)
points = np.array([point+trafo_matrix[:,0]*intercept for point in points])
points = np.array([trafo_mat.dot(point) for point in points])
points = np.array([point-ursprung_translate for point in points])
return points
例如是错误的。我在 Whosebug 上而不是在数学 StackExchange 上问这个问题,因为我认为我无法将相应的数学转换成代码,我很高兴我能做到这一点。
我已经创建了一个 github 要点,其中包含用于执行转换的代码并在 https://gist.github.com/cstenkamp/0fce4d662beb9e07f0878744c7214995, which can be launched using Binder under the link https://mybinder.org/v2/gist/jtpio/0fce4d662beb9e07f0878744c7214995/master?urlpath=lab%2Ftree%2Fchange_of_basis_with_translate.ipynb 处创建绘图,如果有人想使用代码本身的话。
感谢任何帮助!
这里的问题是你的平面是仿射space,不是向量space,所以你不能使用通常的变换矩阵公式。
仿射 space 中的坐标系由原点和基(放在一起称为 仿射框架 )给出。比如你的原点是O,那么M点在仿射坐标系中的坐标就是OM向量在仿射坐标系中的坐标。
如您所见,“正常”R^3 space 是仿射 space 的特例,其中原点为 (0,0,0)。
一旦我们确定了这些,我们就可以在仿射 spaces 中使用框架变化公式:如果我们有两个仿射框架 R = (O, b)
和 R' = (O', b')
,则基础变化公式对于点 M 是:M(R') = base_change_matrix_from_b'_to_b * (M(R) - O'(R))
(O'(R)
O' 在 R 定义的坐标系中的坐标)。
在我们的例子中,我们尝试从原点为 (0,0,0) 的帧出发,然后 规范基础,到一个框架,其中原点是 (0,0,0) 在平面上的正交投影,并且基础是,例如,在您的初始 post.
中描述的那个让我们执行这些步骤:
首先,我们将定义一个 Plane
class 让我们的生活更轻松一些:
from dataclasses import dataclass
import numpy as np
@dataclass
class Plane:
a: float
b: float
c: float
d: float
@property
def normal(self):
return np.array([self.a, self.b, self.c])
def __contains__(self, point:np.array):
return np.isclose(self.a*point[0] + self.b*point[1] + self.c*point[2] + self.d, 0)
def project(self, point):
x,y,z = point
k = (self.a*x + self.b*y + self.c*z + self.d)/(self.a**2 + self.b**2 + self.c**2)
return np.array([x - k*self.a, y-k*self.b, z-k*self.c])
def z(self, x, y):
return (- self.d - self.b*y - self.a*x)/self.c
然后我们可以实现 make_base_changer
,它将 Plane
作为输入,return 2 个 lambda 函数执行正向和反向变换(采用和 return一个点)。你应该可以测试
def normalize(vec):
return vec/np.linalg.norm(vec)
def make_base_changer(plane):
uvec1 = plane.normal
uvec2 = [0, -plane.d/plane.b, plane.d/plane.c]
uvec3 = np.cross(uvec1, uvec2)
transition_matrix = np.linalg.inv(np.array([uvec1, uvec2, uvec3]).T)
origin = np.array([0,0,0])
new_origin = plane.project(origin)
forward = lambda point: transition_matrix.dot(point - new_origin)
backward = lambda point: np.linalg.inv(transition_matrix).dot(point) + new_origin
return forward, backward