我想编写一个程序,给定3D空间中的点列表,表示为浮点的x,y,z坐标数组,在此空间中输出最佳拟合线。该线可以/应该是单位矢量和线上的点的形式。
问题在于我不知道如何做到这一点。我发现最接近的是this链接,但老实说,我不明白他是如何从等式到等式的,当我们得到矩阵时,我很丢失。
是否可以使用简单的二维线性回归的推广/可以有人解释(数学上)上述链接方法的工作原理(以及如何使用它来计算最佳拟合线) )?
答案 0 :(得分:21)
给出了N维线性回归的标准公式
在结果中,是大小为n + 1的向量,给出最适合数据的函数系数。
在你的情况下n = 3.而X是一个mx(n + 1)矩阵,称为设计矩阵 - 在你的情况下是mx4。要构造设计矩阵,只需将每个数据点坐标值(x1,x2,...)复制到X行中,此外,将每行第1列中的数字1放在一行。向量y具有与那些坐标相关联的值。术语和是“X的转置”和“X和X的转置乘积的倒数”。最后一个术语可以计算密集,因为反转矩阵是O(n ^ 3),但对于你n = 4,只要n小于5000,没问题。
假设您有数据点(6,4,11)= 20,(8,5,15)= 30,(12,9,25)= 50和(2,1,3)= 7。 在那种情况下,
然后你只需要将事物相乘,你就可以直接获得。乘法矩阵很简单,虽然更复杂,但采用矩阵的逆矩阵是相当简单的(see here for example)。然而,对于像Matlab,Octave和Julia这样的科学计算语言(我将用它来说明),它是一个单行的。
julia> X = [1 6 4 11; 1 8 5 15; 1 12 9 25; 1 2 1 3]
4x4 Array{Int64,2}:
1 6 4 11
1 8 5 15
1 12 9 25
1 2 1 3
julia> y = [20;30;50;7]
4-element Array{Int64,1}:
20
30
50
7
julia> T = pinv(X'*X)*X'*y
4-element Array{Float64,1}:
4.0
-5.5
-7.0
7.0
...验证
julia> 12*(-5.5) + 9*(-7.0) + 25*(7) + 4
50.0
在Julia中,Matlab和Octave矩阵可以简单地乘以*,而转置运算符是'。注意这里我使用了pinv(伪逆),这是必要的(不是这次),当数据太冗余并产生不可逆的X-Xtranspose时,如果你选择自己实现矩阵求逆,请记住这一点。 / p>
主成分分析(PCA)是一种用于降维的技术,其目的是从n维空间中找到k维空间,使得投影误差最小化。在一般情况下,n和k是任意的,但在这种情况下n = 3且k = 1.有4个主要步骤。
要使标准方法起作用,必须首先执行均值归一化,并且还可能缩放数据,以使算法不会因浮点错误而失败。在后一种情况下,这意味着如果一个维度的值的范围相对于另一个维度是巨大的,则可能存在问题(例如,在一维中-1000到1000对比-0.1到0.2)。通常它们足够接近。均衡化只是意味着每个维度,减去每个数据点的平均值,以便结果数据集以原点为中心。获取结果并将每个数据点(x1,x2,... xn)作为一行存储在一个大矩阵X中。
X = [ 6 4 11; 8 5 15; 12 9 25; 2 1 3]
4x3 Array{Int64,2}:
6 4 11
8 5 15
12 9 25
2 1 3
找到平均值
y = convert(Array{Float64,1},([sum(X[1:4,x]) for x = 1:3])/4')
3-element Array{Float64,1}:
7.0
4.75
13.5
...正常化
julia> Xm = X .- y'
4x3 Array{Float64,2}:
-1.0 -0.75 -2.5
1.0 0.25 1.5
5.0 4.25 11.5
-5.0 -3.75 -10.5
协方差矩阵sigma就是
其中m是数据点的数量。
这里最好找到一个采用协方差矩阵并吐出答案的库。有很多,这里有一些; in python in R,in Java,当然还有Octave,Julia,Matlab(如R),这是另一个 svd 。
在协方差矩阵上执行SVD
(U,S,V) = svd((1/4)*Xm'*Xm);
取第一个组件(对于k维度,您将采用前k个组件)
Ureduce = U[:,1]
3-element Array{Float64,1}:
-0.393041
-0.311878
-0.865015
这是最小化投影误差的线
您甚至可以恢复原始值的近似值,但它们将排成一行并投影在同一条线上。连接点以获得线段。
获取X中每个数据点的缩小尺寸(因为1-D将各自为1个值):
z= Ureduce' * Xm'
1x4 Array{Float64,2}:
2.78949 -1.76853 -13.2384 12.2174
走回去;原始值,但都位于相同(最佳)线
julia> (Ureduce .* z .+ y)'
4x3 Array{Float64,2}:
5.90362 3.88002 11.0871 6 4 11
7.69511 5.30157 15.0298 versus 8 5 15
12.2032 8.87875 24.9514 12 9 25
2.19806 0.939664 2.93176 2 1 3
答案 1 :(得分:1)
为给定的 3D 空间点列表找到最佳拟合线是一项非常困难的任务。 可以使用 2 个向量在 3D 空间中定义一条线:位于线上的点 a 和线(归一化)方向 n 。 可以用下面的等式来描述,其中t是实数
假设有一个点列表{(xᵢ, yᵢ, zᵢ)},点a可以用所有点的平均值表示,即
而方向n可以通过求解协方差矩阵的特征问题来找到
求解特征方程后,可以取最大特征值对应的特征向量,对应于解n。
这是我使用 Armadillo library (C++) 对点集 {(1,1,1), (2,2,2), (3,3,3)} 进行的演示:
#include<armadillo>
#include<vector>
using namespace arma;
int main()
{
std::vector<vec3> points {{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3}
}};
int N = points.size();
vec3 mean = {0, 0, 0};
mat33 corr(fill::zeros);
for(auto p : points)
{
mean += p;
for(int i = 0; i < 3; i++)
for(int j = i; j < 3; j++)
corr(i, j) += p(i) * p(j);
}
corr /= N;
mean /= N;
mat33 cov {{
{corr(0, 0) - mean(0) * mean(0), corr(0, 1) - mean(0) * mean(1), corr(0, 2) - mean(0) * mean(2)},
{corr(0, 1) - mean(0) * mean(1), corr(1, 1) - mean(1) * mean(1), corr(1, 2) - mean(1) * mean(2)},
{corr(0, 2) - mean(0) * mean(2), corr(1, 2) - mean(2) * mean(1), corr(2, 2) - mean(2) * mean(2)}
}};
vec3 eigval; mat33 eigvec;
eig_sym(eigval, eigvec, cov);
mean.print("\nPoint: ");
eigvec.col(eigval.index_max())
.print("\nDirection:");
}
答案 2 :(得分:0)
这可以通过require('ml-matrix')单线实现:
solve(this.DataX, Matrix.columnVector(this.DataY[0]));
答案 3 :(得分:0)
@WaTeim的答案很好
这是我在python中为需要它的人的贡献。与提供的数值示例一起使用
import React from 'react';
import Presentation from "../Presentation";
const data = () => import("../assets/sample.json");
const Finder = (props) => {
const {prop1} = props;
return ( <Presentation data={data} selection={prop1} /> );
};
export default Presentation;
顺便说一句。谁能告诉我为什么numpy的def regr(X):
y= np.average(X, axis=0)
Xm = X-y
u, s, v = np.linalg.svd((1./X.shape[0])*np.matmul(Xm.T,Xm))
# Extra Credit: Going back
z= np.matmul(u[:,0].T, Xm.T)
c = np.array([z*n for n in u[:,0]])
d = np.array(y.tolist()*c.shape[1]).reshape(c.shape[1],-1).T
e = (c+d).T
return u,s,v
regr(np.array([[6, 4, 11],[8,5,15],[12,9,25],[2,1,3]]))
与
np.cov()
???
答案 4 :(得分:0)
我在 QT 代码中使用了这种简单的方法:
QPair<QVector3D, QVector3D> getLineByLeastSquares(const QVector<QVector3D>& points)
{
if (points.size() <= 1)
return QPair<QVector3D, QVector3D>();
QVector3D avg;
for (const QVector3D& p : points)
avg += p;
avg /= static_cast<float>(points.size());
float nX = 0.0F, nY = 0.0F, nZ = 0.0F;
for (const QVector3D& p : points)
{
const QVector3D tmp = p - avg;
nX += tmp.x() * tmp.x();
nY += tmp.x() * tmp.y();
nZ += tmp.x() * tmp.z();
}
return QPair<QVector3D, QVector3D>(avg, QVector3D(nX, nY, nZ).normalized());
}
结果QPair<QVector3D, QVector3D>
的第一个成分是线点,第二个成分是线法线。