R 真是不一般, 首先它不像別的語言, index 是從 0 開始. 而是從 1 開始. 再來呢, 很多地方的 1 看起來是 1 但不是 1; 看起來是 0 但也不是 0; 2 也不是平常的 2. 底下舉例說明.
上次說到用 X predict Y, 像是身高預測. 因為最低身高不是 0, 這條 minimum error 的線是有截距的. 此時一般線性迴歸的斜率公式是:
- 模型:
y = α + βx - 斜率:
β = r(x,y) × sd(y)/sd(x)
假設強制要求通過原點, 他的迴歸如下.
- 模型:
y = βx(無截距項) - 斜率:
β = Σ(xy)/Σ(x²)
最後, 假設原本是有截距的迴歸, 但我把它的數據先做正規化, 那它會跟誰一樣嗎?
x <- c(1.2, 2.8, 3.5, 1.9, 4.1, 2.3, 3.8, 1.6, 2.9, 3.2)
y <- c(2.4, 5.1, 6.8, 3.2, 7.9, 4.1, 7.2, 2.8, 5.5, 6.1)
# 標準化(z-score normalization)
x_norm <- (x - mean(x)) / sd(x)
y_norm <- (y - mean(y)) / sd(y)
# 方法0:一般線性迴歸斜率公式
slope0 <- cor(x, y) * sd(y) / sd(x)
# 方法1:通過原點迴歸
slope1 <- sum(x * y) / sum(x^2)
# 方法2:先正規化後當作一般線性迴歸
slope2 <- cor(x_norm, y_norm) * sd(y_norm) / sd(x_norm)
cat("有截距 - 不通過原點迴歸:", slope0, "\n")
cat("通過原點迴歸:", slope1, "\n")
cat("標準化後做一般回歸:", slope2, "\n")
結果我們會得到 slope0 ≈ 1.85, slope1 ≈ 1.94, slope2 ≈ 0.93, 三個都不一樣.
方法 0 和方法 2 當中, 因為按照定義 :
sd(x_norm) = sd(y_norm) = 1, mean = 0
而 correlation 方面: cor(x,y) 又等於 cor(x_norm, y_norm),
所以 sd(y)/sd(x) ≠ 1 的情況下, 會導致 slope0 ≠ slope2.
方法 1 是強制通過原點, 而原本最佳解可能是有截距的, 所以 slope0 也不太容易巧合和 slope1 、slope2 一樣.
上面講的還沒有發揮到 R 的精神, 我們用 lm() 重做一次.
x <- c(1.2, 2.8, 3.5, 1.9, 4.1, 2.3, 3.8, 1.6, 2.9, 3.2)
y <- c(2.4, 5.1, 6.8, 3.2, 7.9, 4.1, 7.2, 2.8, 5.5, 6.1)
# 標準化
x_norm <- (x - mean(x)) / sd(x)
y_norm <- (y - mean(y)) / sd(y)
# === 手動計算(原始方法) ===
slope0_manual <- cor(x, y) * sd(y) / sd(x)
slope1_manual <- sum(x * y) / sum(x^2)
slope2_manual <- cor(x_norm, y_norm) * sd(y_norm) / sd(x_norm)
# === 使用 lm() 實現 ===
# 方法0:一般線性迴歸(有截距)
model0 <- lm(y ~ x)
slope0_lm <- coef(model0)[2] # 取斜率係數
# 方法1:通過原點迴歸(無截距)
model1 <- lm(y ~ x - 1) # -1 表示移除截距項
slope1_lm <- coef(model1)[1] # 只有一個係數
# 方法2:標準化後的一般迴歸
model2 <- lm(y_norm ~ x_norm)
slope2_lm <- coef(model2)[2] # 取斜率係數
# === 比較結果 ===
cat("=== 手動計算 vs lm() 比較 ===\n")
cat("方法0 - 有截距迴歸:\n")
cat(" 手動計算:", slope0_manual, "\n")
cat(" lm() 結果:", slope0_lm, "\n")
cat(" 差異:", abs(slope0_manual - slope0_lm), "\n\n")
cat("方法1 - 通過原點迴歸:\n")
cat(" 手動計算:", slope1_manual, "\n")
cat(" lm() 結果:", slope1_lm, "\n")
cat(" 差異:", abs(slope1_manual - slope1_lm), "\n\n")
cat("方法2 - 標準化後迴歸:\n")
cat(" 手動計算:", slope2_manual, "\n")
cat(" lm() 結果:", slope2_lm, "\n")
cat(" 差異:", abs(slope2_manual - slope2_lm), "\n\n")
# === 顯示完整模型資訊 ===
cat("=== 完整模型摘要 ===\n")
cat("\n--- 方法0:一般線性迴歸 ---\n")
print(summary(model0))
cat("\n--- 方法1:通過原點迴歸 ---\n")
print(summary(model1))
cat("\n--- 方法2:標準化後迴歸 ---\n")
print(summary(model2))
我們可以看到 model1 <- lm(y ~ x – 1) , 它不是真的減 1, -1 表示移除截距項. 還可以表示為 y ~ 0 + x. 兩者等效.
再來 slope2_lm <- coef(model2)[2] 的 [2] 不是第三個係數. 其中, 第一個係數 [1] 就固定是截距, 第二個係數 [2] 就固定是斜率.
Rrrrrrrrrrrrrrrr 啊啊啊啊啊啊啊啊啊啊….. (聲音漸尖)