【问题标题】:Fitting a line in 3D在 3D 中拟合线
【发布时间】:2013-05-13 05:42:32
【问题描述】:

是否有任何算法可以从一组 3D 数据点返回直线方程?我可以找到很多来源,它们可以从 2D 数据集中给出一条线的方程,但在 3D 中没有。

谢谢。

【问题讨论】:

  • 集合中有多少个点?如果超过 2,你想要最小二乘拟合线吗?你想要什么形式的方程? z = f(x,y) 还是参数?
  • 我应该更具描述性。我想要一条最小二乘参数线。我有来自传感器的 300 个 3D (x,y,z) 数据点,应该在空间中形成一条线。

标签: python numpy linear-algebra curve-fitting


【解决方案1】:

如果您尝试从其他两个值中预测一个值,那么您应该使用 lstsqa 参数作为您的自变量(加上一列 1 来估计截距)和 b 作为您的因变量。

另一方面,如果您只想获得最适合数据的线,即如果您将数据投影到其上,则该线将最小化实点与其投影之间的平方距离,那么你想要的是第一个主成分。

定义它的一种方法是直线,其方向向量是对应于最大特征值的协方差矩阵的特征向量,它穿过数据的平均值。也就是说,eig(cov(data)) 是一种非常糟糕的计算方法,因为它进行了大量不必要的计算和复制,并且可能不如使用 svd 准确。见下文:

import numpy as np

# Generate some data that lies along a line

x = np.mgrid[-2:5:120j]
y = np.mgrid[1:9:120j]
z = np.mgrid[-5:3:120j]

data = np.concatenate((x[:, np.newaxis], 
                       y[:, np.newaxis], 
                       z[:, np.newaxis]), 
                      axis=1)

# Perturb with some Gaussian noise
data += np.random.normal(size=data.shape) * 0.4

# Calculate the mean of the points, i.e. the 'center' of the cloud
datamean = data.mean(axis=0)

# Do an SVD on the mean-centered data.
uu, dd, vv = np.linalg.svd(data - datamean)

# Now vv[0] contains the first principal component, i.e. the direction
# vector of the 'best fit' line in the least squares sense.

# Now generate some points along this best fit line, for plotting.

# I use -7, 7 since the spread of the data is roughly 14
# and we want it to have mean 0 (like the points we did
# the svd on). Also, it's a straight line, so we only need 2 points.
linepts = vv[0] * np.mgrid[-7:7:2j][:, np.newaxis]

# shift by the mean to get the line in the right place
linepts += datamean

# Verify that everything looks right.

import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as m3d

ax = m3d.Axes3D(plt.figure())
ax.scatter3D(*data.T)
ax.plot3D(*linepts.T)
plt.show()

这是它的样子:

【讨论】:

  • 如何产生这条线的渐变?
  • 是否有可能在这种情况下获得多项式拟合......!? @dwf
  • 谁能解释一下为什么主要方向是vv的第一行?我在互联网上发现了相互矛盾的陈述,通常说它应该是对应于最小奇异值的正确奇异值。那将是vv最后行,不是吗?
  • 计算 SVD 在时间和内存上是二次方的。即使只有 10k 点,该方法也需要很多秒,并且出现内存不足错误。有没有更有效的方法可以更有效地计算方向向量?
  • 如果您只需要第一个主成分,那么优化 1 阶近似的均方重构误差将为您提供。谷歌搜索“降级 SVD”应该可以找到东西。这里有一些讨论,在协同过滤的背景下:jmlr.org/papers/volume16/hastie15a/hastie15a.pdf
【解决方案2】:

如果您的数据表现得相当好,那么找到组件距离的最小二乘和应该就足够了。然后你可以找到 z 独立于 x 并再次独立于 y 的线性回归。

documentation 为例:

import numpy as np

pts = np.add.accumulate(np.random.random((10,3)))
x,y,z = pts.T

# this will find the slope and x-intercept of a plane
# parallel to the y-axis that best fits the data
A_xz = np.vstack((x, np.ones(len(x)))).T
m_xz, c_xz = np.linalg.lstsq(A_xz, z)[0]

# again for a plane parallel to the x-axis
A_yz = np.vstack((y, np.ones(len(y)))).T
m_yz, c_yz = np.linalg.lstsq(A_yz, z)[0]

# the intersection of those two planes and
# the function for the line would be:
# z = m_yz * y + c_yz
# z = m_xz * x + c_xz
# or:
def lin(z):
    x = (z - c_xz)/m_xz
    y = (z - c_yz)/m_yz
    return x,y

#verifying:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

fig = plt.figure()
ax = Axes3D(fig)
zz = np.linspace(0,5)
xx,yy = lin(zz)
ax.scatter(x, y, z)
ax.plot(xx,yy,zz)
plt.savefig('test.png')
plt.show()

如果您想最小化从线(与线正交)到 3 空间中的点的实际正交距离(我不确定这甚至称为线性回归)。然后我会构建一个计算 RSS 的函数并使用 scipy.optimize 最小化函数来解决它。

【讨论】:

  • 实际上,您不需要数值优化器——这是一个二次优化问题,可以使用 SVD 以封闭形式轻松解决,请参阅我的答案。 :)
猜你喜欢
  • 2013-09-04
  • 1970-01-01
  • 2011-01-22
  • 2010-10-09
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 2016-04-26
  • 1970-01-01
相关资源
最近更新 更多