2d点的最小二乘拟合不能通过对称轴

时间:2017-08-13 04:32:45

标签: python python-3.x opencv numpy linear-regression

我试图为给定的(x,y)数据点绘制最佳拟合线。

image

这里显示数据点(红色像素)和估计线(绿色),我使用以下库获得。

import numpy as np    
m, c = np.linalg.lstsq(A, y)[0]

已使用library module

的文档

我们可以看到数据点大致对称分布。问题是为什么这条线通过数据点没有类似于长对称轴的梯度?能否解释一下这个结果是否正确?那么,它如何给出最小的误差? (使用lstsq方法返回的渐变正确绘制线条)。谢谢。

修改

以下是我尝试的代码。输入图像可以从here下载。在这段代码中,我没有强迫线穿过像素分布的中心。 (注意:此处我使用polyfit代替lstsq。两者都给出相同的结果)

import numpy as np
import cv2
import math

img = cv2.imread('points.jpg',1);
h, w = img.shape[:2]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

points = np.argwhere(gray>10)    # get (x,y) pairs where red pixels exist
y = points[:,0]
x = points[:,1]

m, c = np.polyfit(x, y, 1)      # calculate least square fit line

# calculate two cordinates (x1,y1),(x2,y2) on the line
angle = np.arctan(m)
x1, y1, length =  0, int(c), 500
x2 =  int(round(math.ceil(x1 + length * np.cos(angle)),0))
y2 =  int(round(math.ceil(y1 + length * np.sin(angle)),0))
# draw line on the color image
cv2.line(img, (x1, y1), (x2, y2), (0,255,0), 1, cv2.LINE_8)
# show output the image
cv2.namedWindow("Display window", cv2.WINDOW_AUTOSIZE);
cv2.imshow("Display window", img);
cv2.waitKey(0);
cv2.destroyAllWindows()

如何让线穿过像素分布的最长对称轴?我可以使用主成分分析吗?

2 个答案:

答案 0 :(得分:3)

很难说为什么会出现这种情况。最重要的是,我无法看到您正在使用的数据,而且我无法查看您正在使用的数据的计算斜率和y截距。

以下是一些可以解释我们所看到的内容的事情: (1)数据点的密度实际上与偶然看起来完全不同,一切都正常。 (2)您向最小二乘函数发送了错误的参数,并且您已经获得了GIGO情况。 (我还没有使用numpy的最小二乘算法,所以我无法检查这个。) (3)散点图和线图不同意轴的比例。 (4)破坏了所讨论的最小二乘函数。 (5)您没有将相同的数据传递给最小二乘算法,因为您正在传递给绘图程序。 (6)数据格式很时髦,因此散点图和最小二乘例程以不同方式解释数据。

我无法知道这些问题中的哪一个,除非是(3),否则我希望我们需要更多的数据才能区分这些可能性。

如果我是你,那么我将如何继续:(1)创建一个位于一条线上的小型人工数据集,并将其传递给最小二乘函数,看看它是否向右吐出数字。看看它们在绘制时是否正确。 (2)如果看起来没问题,记录最小二乘算法的输出,看看你是否能找到另一个最小二乘程序来计算斜率和y截距并进行比较。如果它们相同,那可能不是常规,它可能与绘图有关。

如果你做到这一点并且仍然是一个谜,请告诉我们你发现了什么,也许我们可以提出另一个建议。

祝你好运。

答案 1 :(得分:1)

如果红点真正代表您的数据,您可能会以强制线穿过原点的方式应用线性回归函数。我怎么知道?当对两个变量x和y使用线性回归时,该线将截取几个特定点。例如,x的平均值和y的平均值。此外,根据您的规格,计算或指定y轴的截距。如果x和y的所有变量都是正数,那么如果强制通过原点,那么你将有一条看起来像你的线。在提供可重现的数据和代码之前,不能说更多。

修改 我提供的可重复样本没有太多运气,所以我用随机数建立了一个例子来详细说明我的原始答案。我认为statsmodels是一个适合线性回归分析的库。首先,我将解决之前的评论:

  

如果x和y的所有变量都是正数,那么如果强行穿过原点,你将有一条看起来像你的线。

你会看到越来越多的效果,你的数字越大(你的数字离原点越远)。将sm.OLS(y,sm.add_constant(x)).fit()sm.OLS(y,x).fit()用于两组不同的数字将准确显示我的意思。首先,我将对下面的数据集进行回归,而不是一个估计的常量(该行经过原点)。这将给我们一个类似于你原始情节的情节:

# Libraries
import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt

# Data
np.random.seed(123)
x = np.random.normal(size=2500) + 100
y = x * 2 + np.random.normal(size=2500) + 100

# Regression
results1 = sm.OLS(y,x).fit()
regLine_origin  = x*results1.params[0]

# PLot
fig, ax = plt.subplots()
ax.scatter(x, y, c='red', s=4)
ax.scatter(x, regLine_origin, c = 'green', s = 1)

ax.patch.set_facecolor('black')
plt.show()

enter image description here

接下来,我将在回归中包含一个常量。现在,黄线将代表我认为您在问题中所追求的内容:

# Libraries
import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt

# Data
np.random.seed(123)
x = np.random.normal(size=2500) + 100
y = x * 2 + np.random.normal(size=2500) + 100

# Regression
results1 = sm.OLS(y,x).fit()
results2 = sm.OLS(y,sm.add_constant(x)).fit()
regLine_origin  = x*results1.params[0]
regLine_constant =  results2.params[0] + x*results2.params[1]

# PLot
fig, ax = plt.subplots()
ax.scatter(x, y, c='red', s=4)
ax.scatter(x, regLine_origin, c = 'green', s = 1)
ax.scatter(x, regLine_constant, c = 'yellow', s = 1)

ax.patch.set_facecolor('black')
plt.show()

enter image description here

最后,我们可以看看当数字更接近原点时会发生什么。可以这么说。在这里,我将在生成数字时删除+100部分:

# The following is changed in the snippet above:
# Data
x = np.random.normal(size=2500)
y = x * 2 + np.random.normal(size=2500)

enter image description here

这就是为什么我认为您的原始回归线设置为通过原点。看一下statsmodels包。在这里,您可以运行print(results2.summary())

来研究估算的详细信息

enter image description here

正如您在上面的代码段中所见,您可以使用results2.params直接访问回归系数。

enter image description here

编辑2:我的解释仍然不是100%有效。 x和y值的大小必须略有不同才能看到这种效果。无论数字的大小如何,你肯定会发现线路穿过原点的情况。 看看不同的x标签,你会明白我的意思。