在本章開頭(§3.0),我們提出了關於 Advertising 資料集的七個關鍵問題。經過 §3.1(簡單線性迴歸)、§3.2(多元線性迴歸)和 §3.3(回歸模型的其他考量)的學習後,我們現在可以完整回答這七個問題。本節是一個整合性的案例研究,展示如何將統計學習工具應用於真實的商業決策。
要回答這個問題,我們需要建立一個多元迴歸模型,將銷售額(sales)對三個廣告媒介預算(TV、radio、newspaper)進行回歸:
我們使用 F 統計量來檢定虛無假設 \(H_0: \beta_{\text{TV}} = \beta_{\text{radio}} = \beta_{\text{newspaper}} = 0\)。在 §3.2.2 中,我們知道 F 統計量可以判斷是否應拒絕此虛無假設。Advertising 資料的 F 統計量對應的 p 值非常小,提供了銷售額與廣告預算之間存在關係的明確證據。
我們使用兩個指標來衡量模型的準確度:
殘差標準誤(RSE):估計總體迴歸線的標準差。對 Advertising 資料而言,RSE = 1.69 單位,而銷售額的平均值為 14.022,百分比誤差約為 12%。
決定係數 \(R^2\):記錄預測變數解釋了多少反應變數的變異性。預測變數解釋了銷售額近 90% 的變異。
檢查每個預測變數的 t 統計量對應的 p 值(§3.1.2)。根據 Table 3.4 的多元迴歸結果:
| 預測變數 | p 值 | 顯著性 |
|---|---|---|
| TV | < 0.0001 | ✅ 高度顯著 |
| radio | < 0.0001 | ✅ 高度顯著 |
| newspaper | 0.860 | ❌ 不顯著 |
只有 TV 和 radio 與銷售額相關。newspaper 的 p 值不顯著,表示在控制 TV 和 radio 後,newspaper 對銷售額沒有額外的解釋力。
利用 §3.1.2 中 \(\hat{\beta}_j\) 的標準誤,我們可以構建 \(\beta_j\) 的 95% 信賴區間。根據 Table 3.4 的結果:
| 預測變數 | 95% 信賴區間 | 解讀 |
|---|---|---|
| TV | (0.043, 0.049) | 窄且遠離零 → 顯著正向影響 |
| radio | (0.172, 0.206) | 窄且遠離零 → 顯著正向影響 |
| newspaper | (−0.013, 0.011) | 包含零 → 統計上不顯著 |
TV 和 radio 的信賴區間窄且遠離零,提供了這些媒介與銷售額相關的強力證據。newspaper 的信賴區間包含零,表示在 TV 和 radio 存在的情況下,newspaper 統計上不顯著。
為了個別評估每個媒介對銷售額的影響,我們可以執行三個簡單線性迴歸。結果顯示 TV 和 radio 與銷售額之間存在極強的關聯,而 newspaper 與銷售額之間只有微弱的關聯(當忽略 TV 和 radio 時)。
使用多元迴歸模型進行預測時,預測的準確度取決於我們想要預測的是什麼:
| 預測目標 | 使用工具 | 寬度 |
|---|---|---|
| 個別反應值 \(Y = f(X) + \epsilon\) | 預測區間(Prediction Interval) | 較寬 |
| 平均反應值 \(f(X)\) | 信賴區間(Confidence Interval) | 較窄 |
在 §3.3.3 中,我們學到殘差圖可以用來識別非線性關係。如果關係是線性的,殘差圖應該呈現隨機散布、無明顯模式。
對 Advertising 資料,Figure 3.5 觀察到一些非線性效應。這種非線性效應也可以在殘差圖中觀察到。在 §3.3.2 中,我們討論了如何在線性迴歸模型中加入預測變數的轉換(如多項式項)來處理非線性關係。
標準線性迴歸模型假設預測變數與反應變數之間是加法關係(additive relationship)。加法模型容易解釋,因為每個預測變數與反應變數的關聯與其他預測變數的值無關。
然而,加法假設可能不切實際。Figure 3.5 暗示 Advertising 資料可能不是加法的。加入交互作用項後,\(R^2\) 從約 90% 大幅提升到近 97%!
以下程式碼完整回答 Advertising 資料集的七個行銷問題:
try:
from google.colab import drive
drive.mount('/content/drive')
DATA_PATH = '/content/drive/MyDrive/ISLP_data/'
except ImportError:
DATA_PATH = '/tmp/'
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.stats.outliers_influence import variance_inflation_factor as VIF
from itertools import combinations
# 載入 Advertising 資料
try:
adv = pd.read_csv(DATA_PATH + 'Advertising.csv')
except FileNotFoundError:
import urllib.request
urllib.request.urlretrieve(
'https://www.statlearning.com/s/Advertising.csv',
'/tmp/Advertising.csv'
)
adv = pd.read_csv('/tmp/Advertising.csv')
print("=== Advertising 資料集前 5 筆 ===")
print(adv.head())
print(f"\n資料筆數: {len(adv)}")
print(f"\n基本統計量:\n{adv.describe()}")
# === 問題 1 & 2 & 3 & 4: 多元迴歸分析 ===
print("\n" + "="*60)
print("問題 1~4: 多元迴歸分析")
print("="*60)
X = adv[['TV', 'radio', 'newspaper']]
X_const = sm.add_constant(X)
y = adv['sales']
model = sm.OLS(y, X_const).fit()
print(model.summary())
# F 統計量 (問題 1)
print(f"\n【問題 1】F 統計量 = {model.fvalue:.2f}, p 值 = {model.f_pvalue:.2e}")
print(f" → p 值極低,拒絕 H0,至少有一個預測變數與 sales 相關")
# RSE 和 R² (問題 2)
rse = np.sqrt(model.mse_resid)
r2 = model.rsquared
print(f"\n【問題 2】RSE = {rse:.4f}, R² = {r2:.4f}")
print(f" → 百分比誤差 = {rse/adv['sales'].mean()*100:.1f}%")
print(f" → 預測變數解釋了 {r2*100:.1f}% 的 sales 變異")
# p 值 (問題 3)
print(f"\n【問題 3】各預測變數的 p 值:")
for var in ['TV', 'radio', 'newspaper']:
pval = model.pvalues[var]
sig = "顯著" if pval < 0.05 else "不顯著"
print(f" {var}: p = {pval:.4f} → {sig}")
# 95% 信賴區間 (問題 4)
print(f"\n【問題 4】95% 信賴區間:")
ci = model.conf_int(alpha=0.05)
for var in ['TV', 'radio', 'newspaper']:
lo, hi = ci.loc[var]
contains_zero = "包含零 (不顯著)" if lo <= 0 <= hi else "不含零 (顯著)"
print(f" {var}: ({lo:.4f}, {hi:.4f}) → {contains_zero}")
# VIF 檢查
print(f"\n VIF 值:")
for i, var in enumerate(['const', 'TV', 'radio', 'newspaper']):
if var == 'const':
continue
vif_val = VIF(X_const.values, list(X_const.columns).index(var))
print(f" {var}: VIF = {vif_val:.3f}")
# === 問題 5: 預測 ===
print("\n" + "="*60)
print("問題 5: 預測新觀測值")
print("="*60)
# 預測 TV=100, radio=20, newspaper=0
new_obs = pd.DataFrame({'TV': [100], 'radio': [20], 'newspaper': [0]})
new_obs_const = sm.add_constant(new_obs, has_constant='add')
pred = model.get_prediction(new_obs_const)
pred_summary = pred.summary_frame(alpha=0.05)
print(f"\n預測條件: TV=100, radio=20, newspaper=0")
print(f"預測值 (mean): {pred_summary['mean'].values[0]:.4f}")
print(f"信賴區間 (CI): ({pred_summary['mean_ci_lower'].values[0]:.4f}, {pred_summary['mean_ci_upper'].values[0]:.4f})")
print(f"預測區間 (PI): ({pred_summary['obs_ci_lower'].values[0]:.4f}, {pred_summary['obs_ci_upper'].values[0]:.4f})")
print(f"\n→ 預測區間比信賴區間寬,因為包含了不可約誤差 ε")
# === 問題 6: 非線性檢查 ===
print("\n" + "="*60)
print("問題 6: 殘差圖檢查線性假設")
print("="*60)
fig, axes = plt.subplots(1, 3, figsize=(14, 4))
for ax, var in zip(axes, ['TV', 'radio', 'newspaper']):
ax.scatter(adv[var], model.resid, alpha=0.5, s=15)
ax.axhline(0, c='red', linestyle='--')
ax.set_xlabel(var)
ax.set_ylabel('Residual')
ax.set_title(f'Residuals vs {var}')
plt.tight_layout()
plt.savefig('/tmp/residual_plots_adv.png', dpi=100, bbox_inches='tight')
plt.show()
print("殘差圖已儲存 → 若殘差呈現隨機散布則線性假設成立")
# === 問題 7: 交互作用效應 ===
print("\n" + "="*60)
print("問題 7: 交互作用效應 (radio × TV)")
print("="*60)
adv['radio_TV'] = adv['radio'] * adv['TV']
X_interact = adv[['TV', 'radio', 'radio_TV']]
X_interact_const = sm.add_constant(X_interact)
model_interact = sm.OLS(y, X_interact_const).fit()
print(f"\n無交互作用模型 R² = {model.rsquared:.4f}")
print(f"含交互作用模型 R² = {model_interact.rsquared:.4f}")
print(f"R² 提升: {(model_interact.rsquared - model.rsquared)*100:.2f}%")
print(f"\n交互作用項係數: {model_interact.params['radio_TV']:.6f}")
print(f"交互作用項 p 值: {model_interact.pvalues['radio_TV']:.4e}")
print(f"\n→ 交互作用項高度顯著,表示 radio 和 TV 之間存在協同效應")
print(f"→ R² 從 {model.rsquared*100:.1f}% 提升到 {model_interact.rsquared*100:.1f}%")
# 視覺化交互作用
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
tv_levels = [10, 50, 100, 200]
for tv_val in tv_levels:
radio_range = np.linspace(0, 50, 100)
pred_sales = (model_interact.params['const'] +
model_interact.params['TV'] * tv_val +
model_interact.params['radio'] * radio_range +
model_interact.params['radio_TV'] * tv_val * radio_range)
axes[0].plot(radio_range, pred_sales, label=f'TV={tv_val}')
axes[0].set_xlabel('Radio Budget')
axes[0].set_ylabel('Predicted Sales')
axes[0].set_title('Interaction Effect: Sales vs Radio at different TV levels')
axes[0].legend()
# 殘差圖
axes[1].scatter(model_interact.fittedvalues, model_interact.resid, alpha=0.5, s=15)
axes[1].axhline(0, c='red', linestyle='--')
axes[1].set_xlabel('Fitted values')
axes[1].set_ylabel('Residuals')
axes[1].set_title('Residuals: Interaction Model')
plt.tight_layout()
plt.savefig('/tmp/interaction_effect.png', dpi=100, bbox_inches='tight')
plt.show()
print("交互作用圖已儲存")
一家電商公司有年度行銷預算 500 萬,需要分配到 TV、radio 和社群媒體三個管道。利用多元迴歸分析:
醫院想要了解不同治療方案(藥物 A、藥物 B、物理治療)對患者復原時間的影響。類似的分析框架:
| 分析步驟 | 使用的工具 | 替代方法 | 適用情境 |
|---|---|---|---|
| 關係存在性 | F 統計量 | ANOVA、卡方檢定 | 多個預測變數的整體檢定 |
| 模型強度 | RSE, R² | Adjusted R², AIC, BIC | 模型選擇與比較 |
| 個別顯著性 | t 統計量 | Likelihood Ratio Test | 單一預測變數的檢定 |
| 效應大小 | 信賴區間 | 效果量 (Cohen's d) | 量化影響幅度 |
| 預測 | 預測/信賴區間 | Bootstrap、Cross-validation | 新觀測值的預測 |
| 非線性偵測 | 殘差圖 | 成分殘差圖、GAM | 模型假設驗證 |
| 交互作用 | 交互作用項 | 樹模型、MARS | 非加法關係 |
| # | 問題 | 答案 | 關鍵證據 |
|---|---|---|---|
| 1 | 是否存在關係? | 是 | F 檢定 p 值 < 0.0001 |
| 2 | 關係多強? | R² ≈ 90% | RSE = 1.69 (12% 誤差) |
| 3 | 哪些媒介相關? | TV, radio | newspaper p 值 = 0.86 |
| 4 | 關聯多大? | CI 不含零 (TV, radio) | VIF ≈ 1,無共線性 |
| 5 | 預測準確度? | PI 比 CI 寬 | 包含不可約誤差 ε |
| 6 | 線性關係? | 輕微非線性 | 殘差圖有模式 |
| 7 | 協同效應? | 是 (radio × TV) | R² 從 90% → 97% |