演習問題 9.3

(a)

library(MASS)      # ginv など
library(mvtnorm)   # 多変量正規分布からのサンプリング
library(readr)     # read_delim
library(dplyr)     # データフレーム操作
library(tibble)

# データ読み込み
df <- read_table("crime.dat", col_names = TRUE)

# 説明変数と目的変数に分割
y <- df$y
X <- df %>% select(-y) %>% as.matrix()
X <- cbind(1, X)  # 切片を追加
n <- nrow(X)
p <- ncol(X)

# 最小二乗による残差分散推定
ResidualSE <- function(y, X) {
  fit <- lm(y ~ X - 1)  # X には既に切片があるので -1
  rss <- sum(resid(fit)^2)
  return(rss / (n - p))
}

# g-prior によるベイズ回帰サンプリング
lmGprior <- function(y, X, g = n, nu0 = 1, sigma0_sq = ResidualSE(y, X), S = 1000) {
  XtX_inv <- ginv(t(X) %*% X)
  Hg <- (g / (g + 1)) * X %*% XtX_inv %*% t(X)
  SSRg <- t(y) %*% (diag(n) - Hg) %*% y
  sigma_sq_samples <- 1 / rgamma(S, shape = (nu0 + n) / 2, rate = (nu0 * sigma0_sq + SSRg) / 2)

  Vb <- g / (g + 1) * XtX_inv
  Eb <- Vb %*% t(X) %*% y

  beta_samples <- matrix(0, nrow = S, ncol = p)
  for (i in 1:S) {
    s <- sigma_sq_samples[i]
    beta_samples[i, ] <- rmvnorm(1, mean = as.vector(Eb), sigma = s * Vb)
  }

  list(beta = beta_samples, sigma_sq = sigma_sq_samples)
}

# パラメータ設定と実行
g <- n
nu0 <- 2
sigma0_sq <- 1
S <- 5000
res <- lmGprior(y, X, g, nu0, sigma0_sq, S)
beta_samples <- res$beta

# 事後平均と信用区間
beta_mean <- colMeans(beta_samples)
ci <- apply(beta_samples, 2, quantile, probs = c(0.025, 0.975))

# 結果をデータフレームにまとめる
param_names <- c("intercept", colnames(df)[colnames(df) != "y"])
result <- tibble(
  param = param_names,
  beta_hat = beta_mean,
  ci_lower = ci[1, ],
  ci_upper = ci[2, ],
  ci_length = ci[2, ] - ci[1, ]
)

print(result)
# A tibble: 16 × 5
   param      beta_hat ci_lower ci_upper ci_length
   <chr>         <dbl>    <dbl>    <dbl>     <dbl>
 1 intercept -0.000194  -0.141    0.142      0.283
 2 M          0.281      0.0320   0.532      0.500
 3 So        -0.00311   -0.325    0.331      0.657
 4 Ed         0.534      0.224    0.859      0.636
 5 Po1        1.43      -0.0351   2.91       2.94 
 6 Po2       -0.760     -2.27     0.779      3.05 
 7 LF        -0.0657    -0.347    0.213      0.560
 8 M.F        0.133     -0.147    0.409      0.555
 9 Pop       -0.0675    -0.298    0.160      0.458
10 NW         0.108     -0.189    0.420      0.609
11 U1        -0.269     -0.626    0.0901     0.717
12 U2         0.363      0.0471   0.683      0.636
13 GDP        0.236     -0.228    0.709      0.937
14 Ineq       0.715      0.289    1.14       0.856
15 Prob      -0.278     -0.520   -0.0270     0.493
16 Time      -0.0597    -0.299    0.180      0.479
# OLS(最小二乗法)回帰モデル
olsfit <- lm(y ~ X - 1)  # Xには切片が含まれているので -1 を指定

# 回帰係数の要約統計量を抽出
summary_fit <- summary(olsfit)
coef_summary <- summary_fit$coefficients

# 名前の取得(同じく X に切片が入っている前提)
coef_name <- c("intercept", colnames(df)[colnames(df) != "y"])

# 信頼区間(デフォルトで95%)
ci <- confint(olsfit)

# 結果をデータフレームにまとめる
result_ols <- tibble(
  param = coef_name,
  beta_hat = coef_summary[, "Estimate"],
  pvalue = coef_summary[, "Pr(>|t|)"],
  ci_lower = ci[, 1],
  ci_upper = ci[, 2],
  ci_length = ci[, 2] - ci[, 1]
)

print(result_ols)
# A tibble: 16 × 6
   param      beta_hat  pvalue ci_lower ci_upper ci_length
   <chr>         <dbl>   <dbl>    <dbl>    <dbl>     <dbl>
 1 intercept -0.000458 0.995   -0.161     0.161      0.322
 2 M          0.287    0.0430   0.00956   0.563      0.554
 3 So        -0.000114 1.00    -0.375     0.375      0.751
 4 Ed         0.545    0.00488  0.178     0.911      0.733
 5 Po1        1.47     0.0813  -0.194     3.14       3.33 
 6 Po2       -0.782    0.366   -2.52      0.955      3.47 
 7 LF        -0.0660   0.670   -0.379     0.247      0.626
 8 M.F        0.131    0.404   -0.185     0.448      0.633
 9 Pop       -0.0703   0.584   -0.329     0.189      0.518
10 NW         0.109    0.531   -0.242     0.460      0.701
11 U1        -0.271    0.179   -0.672     0.130      0.802
12 U2         0.369    0.0489   0.00191   0.736      0.734
13 GDP        0.238    0.365   -0.290     0.766      1.06 
14 Ineq       0.726    0.00408  0.249     1.20       0.955
15 Prob      -0.285    0.0410  -0.558    -0.0124     0.546
16 Time      -0.0616   0.642   -0.329     0.206      0.534

95%信用区間/信頼区間の長さについて、ベイズ回帰の方が最小二乗推定よりも短い。また、ベイズ回帰の結果を見ると、M、Ed、U2、Ineqの係数の95%信用区間には0が含まれていないため、これらの変数は犯罪率の強い予測因子であると考えられる一方、最小二乗推定の結果では、M、Ed、U2、Ineq、Probのp値が0.05未満であるため、これらの変数が犯罪率の強い予測因子であると考えられる。

(b)

set.seed(1234)  # 乱数シード固定

n <- nrow(X)  # 全データ数
i_te <- sample(1:n, n %/% 2, replace = FALSE)   # テスト用インデックス
i_tr <- setdiff(1:n, i_te)                      # 学習用インデックス

# データ分割
X_tr <- X[i_tr, , drop = FALSE]
X_te <- X[i_te, , drop = FALSE]
y_tr <- y[i_tr]
y_te <- y[i_te]

# OLSモデル学習
olsfit <- lm(y_tr ~ X_tr - 1)  # X_tr に切片が含まれている前提なので -1

# 係数取得 → 予測 → MSE計算
beta_hat <- coef(olsfit)
y_hat <- X_te %*% beta_hat
error_ols <- mean((y_te - y_hat)^2)

print(error_ols)

library(ggplot2)

df_plot <- data.frame(
  Actual = y_te,
  Predicted = as.vector(y_hat)
)

# 散布図 + 対角線(理想線)
ggplot(df_plot, aes(x = Actual, y = Predicted)) +
  geom_point(color = "steelblue") +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
  labs(title = "OLS Predictions vs Actual",
       x = "Actual y (Test Data)",
       y = "Predicted y") +
  theme_minimal()
[1] 1.261635

(a)

# ベイズ回帰のサンプリング
g <- nrow(X_tr)
bayes_fit <- lmGprior(y_tr, X_tr, g, nu0, sigma0_sq, S)

# 事後平均の回帰係数
beta_hat_bayes <- colMeans(bayes_fit$BETA)

# テストデータで予測
y_hat_bayes <- X_te %*% beta_hat_bayes

# 平均二乗誤差 (MSE)
error_bayes <- mean((y_te - y_hat_bayes)^2)
print(error_bayes)

df_bayes_plot <- data.frame(
  Actual = y_te,
  Predicted = as.vector(y_hat_bayes)
)

ggplot(df_bayes_plot, aes(x = Actual, y = Predicted)) +
  geom_point(color = "darkgreen") +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
  labs(title = "Bayesian Regression Predictions vs Actual",
       x = "Actual y (Test Data)",
       y = "Predicted y") +
  theme_minimal()
[1] 1.2272

(b)

最小二乗法とベイズ法は、テスト・データについて非常によく似た予測をもたらすように見える。今回、ベイズ法は最小二乗法よりも精度が高いが、その差はとても小さい。

(c)

# データ分割関数
splitData <- function(y, X) {
  n <- nrow(X)
  i_te <- sample(1:n, n %/% 2, replace = FALSE)
  i_tr <- setdiff(1:n, i_te)
  list(
    y_tr = y[i_tr],
    y_te = y[i_te],
    X_tr = X[i_tr, , drop = FALSE],
    X_te = X[i_te, , drop = FALSE]
  )
}

# 残差分散推定
ResidualSE <- function(y, X) {
  n <- nrow(X)
  p <- ncol(X)
  fit <- lm(y ~ X - 1)
  rss <- sum(resid(fit)^2)
  rss / (n - p)
}

# MSEを計算する関数
computeMses <- function(y, X, g, nu0, sigma0_sq, S) {
  split <- splitData(y, X)
  y_tr <- split$y_tr
  y_te <- split$y_te
  X_tr <- split$X_tr
  X_te <- split$X_te

  # OLS
  ols_fit <- lm(y_tr ~ X_tr - 1)
  beta_hat_ols <- coef(ols_fit)
  y_hat_ols <- X_te %*% beta_hat_ols
  error_ols <- mean((y_te - y_hat_ols)^2)

  # Bayesian
  bayes_fit <- lmGprior(y_tr, X_tr, g, nu0, sigma0_sq, S)
  beta_hat_bayes <- colMeans(bayes_fit$beta)
  y_hat_bayes <- X_te %*% beta_hat_bayes
  error_bayes <- mean((y_te - y_hat_bayes)^2)

  c(error_ols = error_ols, error_bayes = error_bayes)
}

# 実行設定
set.seed(1234)
n_sim <- 1000
error_ols <- numeric(n_sim)
error_bayes <- numeric(n_sim)

g <- nrow(X)
n <- nrow(X)

# シミュレーション
for (i in 1:n_sim) {
  errors <- computeMses(y, X, g, nu0, sigma0_sq, S)
  error_ols[i] <- errors["error_ols"]
  error_bayes[i] <- errors["error_bayes"]
}

# 平均MSEの表示
cat("平均OLS MSE: ", mean(error_ols), "\n")
cat("平均Bayesian MSE: ", mean(error_bayes), "\n")
> cat("平均OLS MSE: ", mean(error_ols), "\n")
平均OLS MSE:  0.1485031 
> cat("平均Bayesian MSE: ", mean(error_bayes), "\n")
平均Bayesian MSE:  0.1407067