Reputation: 486
I am building an example to show, graphically, how the least square method works. I am applying a numerical approach where I feed R a number of combinations of possible values of intercept (a) and slope (b), then compute the sum of squares (SSE) for all possible combinations. The combinations of a and b with associated the lowest SSE should be the best one, but somehow my estimates of a are always off mark compared to the real value computed by lm(). On top of that, My estimate of a is sensitive to the range of possible values of a given to R - the broader the range, the more the estimate of a is off.
Here is my example. I'm using the dataset "longley", built in R:
data(longley)
plot(GNP ~ Employed, data = longley,
xlab="% employed adults",
ylab="Gross National Product (million $?)",
main="Money money money"
)
# ranges of a and be where we think their true value lies:
possible.a.vals <- seq(-1431,-1430, by=0.01)
possible.b.vals <- seq(27,28.5, by=0.01)
# all possible combinations of a and b:
possible.ab <- expand.grid(possible.a.vals = possible.a.vals,
possible.b.vals = possible.b.vals
)
possible.ab.SSE <- as.data.frame(possible.ab)
head(possible.ab.SSE); tail(possible.ab.SSE)
possible.ab.SSE$SSE <- rep(NA, length.out = length(possible.ab.SSE[,1]))
for (i in 1:length(possible.ab.SSE[,1])){
predicted.GNP <- possible.ab.SSE$possible.a.vals[i] + possible.ab.SSE$possible.b.vals[i] * longley$Employed
possible.ab.SSE$SSE[i] <- sum((longley$GNP - predicted.GNP)^2)
}
possible.ab.SSE$possible.a.vals[which(possible.ab.SSE$SSE == min(possible.ab.SSE$SSE))]
possible.ab.SSE$possible.b.vals[which(possible.ab.SSE$SSE == min(possible.ab.SSE$SSE))]
# Estimate of a = -1430.73
# estimate of b = 27.84
# True values of a and b:
# a = -1430.48
# b = 27.84
My estimate of b is spot on but a is slightly off. Moreover, extending the range of possible values of a and b produces estimates of a that are even further from the real value, giving me an estimate of a around -1428 - besides making my loop work forever, which I could solve by using apply() if I weren't a lazy ass.
# plot in 3d:
require(akima) # this helps interpolating the values of a,b, and SSE to create a surface
x= possible.ab.SSE$possible.a.vals
y= possible.ab.SSE$possible.b.vals
z=possible.ab.SSE$SSE
s=interp(x,y,z)
persp(x = s$x,
y = s$y,
z = s$z,
theta =50, phi = 10,
xlab="a", ylab="b", zlab="SSE",
box=T
)
This suggests that the correlation between sum of squares and possible a values is roughly flat, which explains why estimates of a tend to be off mark. This still puzzles me: if the analytical approach to the Least Squares method nails the estimates of the parameter values, so should a numerical approach.
Should it not?
Thank you in advance for your feedback.
EDIT
It's been pointed out that the issue is a resolution one. I overlooked that the value of SSE associated to each value of a is not independent from b; on top of that, changes in SSE are more strongly affected by changes in b than they are by changes in a (or at least that's my understanding of it). The result is that approximations in the estimated value of the slope b can thow off the estimate of the intercept a.
Here are three graphs showing the correlation between a, b, and SSE for broader and sparser ranges of values:
possible.a.vals <- seq(-3000,1000, by=10)
possible.b.vals <- seq(-30,60, by=2)
Upvotes: 3
Views: 206
Reputation: 13128
@ben-bolker is right. It is not entirely correct to say that your "estimate of b is spot on." The difference between the value that minimizes SSE in your example, 27.84
, and the OLS estimate, 27.83626
, turns out to significantly affect the intercept estimate.
data(longley)
# ranges of a and be where we think their true value lies:
possible.a.vals <- seq(-1431,-1430, by = 0.005)
possible.b.vals <- seq(27.5,28, by = 0.00001)
# all possible combinations of a and b:
possible.ab.SSE <- expand.grid(possible.a.vals = possible.a.vals,
possible.b.vals = possible.b.vals)
possible.ab.SSE <- as.matrix(possible.ab.SSE)
out <- tcrossprod(cbind(1, longley$Employed), possible.ab.SSE)
possible.ab.SSE <- as.data.frame(possible.ab.SSE)
possible.ab.SSE$SSE <- colSums((out - longley$GNP)^2)
possible.ab.SSE[order(possible.ab.SSE$SSE), ][1, ]
# possible.a.vals possible.b.vals SSE
# 6758127 -1430.48 27.83622 4834.891
coef(lm(GNP ~ Employed, data = longley))
# (Intercept) Employed
# -1430.48231 27.83626
Upvotes: 1