# 準備 以下で利用する共通パッケージを読み込む. ```{r} library(conflicted) # 関数名の衝突を警告 conflicts_prefer( # 優先的に使う関数を指定 dplyr::filter(), dplyr::select(), dplyr::lag(), ) library(tidyverse) library(GGally) library(broom) # 解析結果を tibble 形式に集約 library(ggfortify) # 診断プロットの描画 library(gt) # 表の作成 library(gtsummary) # 分析結果の表の作成 ``` # 回帰式を用いた予測 ## あてはめ値と予測値 予測は関数 `broom::augment()` を用いて行うことができる. ``` r #' モデルの作成 toy_train <- tibble(x1 = ..., x2 = ..., y = ...) toy_lm <- lm(y ~ x1 + x2, data = toy_train) toy_train_fitted <- augment(toy_lm) # あてはめ値の計算 #' 新しいデータの予測 toy_test <- tibble(x1 = ..., x2 = ...) # 予測したいデータの説明変数 toy_test_fitted <- augment(toy_lm, # 予測値の計算 newdata = toy_test) toy_test_conf <- augment(toy_lm, newdata = toy_test, # 信頼区間 interval = "confidence", conf.level = 0.95) toy_test_pred <- augment(toy_lm, newdata = toy_test, # 予測区間 interval = "prediction", conf.level = 0.95) ``` 信頼区間,予測区間の水準の既定値は0.95である. ## 東京の気候データによる例 9,10月のデータでモデルを構築し,8,11月のデータを予測する. ```{r} #' データの整理 tw_data <- read_csv("data/tokyo_weather.csv") tw_train <- tw_data |> # モデル推定用データ filter(month %in% c(9,10)) # %in% は集合に含むかどうかを判定 tw_test <- tw_data |> # 予測用データ filter(month %in% c(8,11)) #' モデルの構築 tw_model <- temp ~ solar + press # モデルの定義 tw_lm <- lm(tw_model, data = tw_train) # モデルの推定 tidy(tw_lm) # 回帰係数の評価 glance(tw_lm) # モデルの評価 #' あてはめ値と予測値の計算 tw_train_fitted <- augment(tw_lm, newdata = tw_train) # あてはめ値 tw_test_fitted <- augment(tw_lm, newdata = tw_test) # 予測値 ``` グラフでの表示の例は以下のようになる. ```{r} #' 予測結果を図示 bind_rows(tw_train_fitted, tw_test_fitted) |> # 2つのデータフレームを結合 mutate(month = as_factor(month)) |> # 月を因子化して表示に利用 ggplot(aes(x = .fitted, y = temp)) + geom_point(aes(colour = month, shape = month)) + # 月ごとに色と形を変える geom_abline(slope = 1, intercept = 0, # 予測が完全に正しい場合のガイド線 colour = "gray") + labs(x = "fitted", y = "observed") ``` ::: column-margin 関数 `lubridate::month()` を用いると月を文字列のラベルとすることもできる. ```{r} bind_rows(tw_train_fitted, tw_test_fitted) |> mutate(month = month(month, label = TRUE)) |> # 文字にする場合 ggplot(aes(x = .fitted, y = temp)) + geom_point(aes(colour = month)) + # 月ごとに色を変える geom_abline(slope = 1, intercept = 0, # 予測が完全に正しい場合のガイド線 colour = "gray") + labs(x = "fitted", y = "observed") ``` この場合は順序付きの因子になるので,'shape' を利用すると警告が出る. ::: ## 区間表示のための関数 関数 `ggplot2::geom_errorbar()` を用いると 区間を図示することができる. ``` r geom_errorbar( mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., na.rm = FALSE, orientation = NA, show.legend = NA, inherit.aes = TRUE ) #' mapping: 区間を表すために xmin,xmax または ymin,ymax を与える #' data: データフレーム #' ...: その他の描画オプション #' orientation: 特別な場合に指定 (一般に向きは mapping で自動的決定) #' 詳細は ?ggplot2::geom_errorbar ``` 関数 `broom::augment()` の出力の `.lower/.upper` 列を用いればよい. ## 問題 東京の気候データを用いて以下の実験を試みなさい. - 8月のデータで回帰式を推定する - 上記のモデルで9月のデータを予測する 特定の月のデータを取り出すには,例えば以下のようにすればよい ``` r tw_data <- read_csv("data/tokyo_weather.csv") tw_train <- tw_data |> filter(month == 8) # 単一の数字と比較 tw_test <- tw_data |> filter(month %in% c(9,10)) # 集合と比 ``` ## 解答欄 ```{r} #' ここにコードを書く ``` ## 解答例 東京の気候データによる分析において,信頼区間と予測区間の計算を行う. 推定用データと予測用データを準備し,推定用データを用いて回帰式の推定を行う. ```{r} #| code-fold: true tw_data <- read_csv("data/tokyo_weather.csv") tw_train <- tw_data |> filter(month %in% 8) # 推定用データ tw_test <- tw_data |> filter(month %in% 9) # 予測用データ tw_model <- temp ~ solar + press + cloud # モデルの定義 tw_lm <- lm(tw_model, data = tw_train) # モデルの推定 ``` 信頼区間の計算を行う. ```{r} #| code-fold: true tw_train_conf <- augment(tw_lm, newdata = tw_train, # 推定用データに interval = "confidence") # あてはめ値と信頼区間を付加 tw_test_conf <- augment(tw_lm, newdata = tw_test, # 予測用データ(新規データ)に interval = "confidence") # あてはめ値と信頼区間を付加 ``` 予測区間の計算を行う. ```{r} #| code-fold: true tw_train_pred <- augment(tw_lm, newdata = tw_train, # 推定用データに interval = "prediction") # あてはめ値と予測区間を付加 tw_test_pred <- augment(tw_lm, newdata = tw_test, # 予測用データ(新規データ)に interval = "prediction") # あてはめ値と予測区間を付加 ``` 8月のデータで推定したモデルで8月をあてはめた信頼区間を描画する. ```{r} #| code-fold: true tw_train_conf |> ggplot(aes(x = day, y = temp)) + geom_point(colour = "red", shape = 16) + geom_point(aes(y = .fitted), colour = "blue") + geom_errorbar(aes(ymin = .lower, ymax = .upper), colour = "royalblue") + ylim(c(20,34)) + # 以降比較のため4つのグラフの値域を揃える labs(x = "August", y = "Temperature", title = "Confidence Interval") ``` 8月のデータで推定したモデルで8月をあてはめた予測区間を描画する. ```{r} #| code-fold: true tw_train_pred |> ggplot(aes(x = day, y = temp)) + geom_point(colour = "red", shape = 16) + geom_point(aes(y = .fitted), colour = "blue") + geom_errorbar(aes(ymin = .lower, ymax = .upper), colour = "steelblue") + ylim(c(20,34)) + labs(x = "August", y = "Temperature", title = "Prediction Interval") ``` 8月のモデルで9月をあてはめた信頼区間を描画する. ```{r} #| code-fold: true tw_test_conf |> ggplot(aes(x = day, y = temp)) + geom_point(colour = "red", shape = 16) + geom_point(aes(y = .fitted), colour = "blue") + geom_errorbar(aes(ymin = .lower, ymax = .upper), colour = "royalblue") + ylim(c(20,34)) + labs(x = "September", y = "Temperature", title = "Confidence Interval") ``` 8月のモデルで9月をあてはめた予測区間を描画する. ```{r} #| code-fold: true tw_test_pred |> ggplot(aes(x = day, y = temp)) + geom_point(colour = "red", shape = 16) + geom_point(aes(y = .fitted), colour = "blue") + geom_errorbar(aes(ymin = .lower, ymax = .upper), colour = "steelblue") + ylim(c(20,34)) + labs(x = "September", y = "Temperature", title = "Prediction Interval") ``` # 非線形性を含むモデルとカテゴリカル変数の扱い 線形でないモデル式を記述するためには特殊な記法がある. - 非線形変換はそのまま関数を記述すればよい. - 1つの変数の多項式は関数 `I()` を用いる. ## 線形でないモデル式の書き方 ``` r #' 目的変数 Y, 説明変数 X1,X2,X3 #' 交互作用を含む式 (formula) の書き方 Y ~ X1 + X1:X2 # X1 + X1*X2 Y ~ X1 * X2 # X1 + X2 + X1*X2 Y ~ (X1 + X2 + X3)^2 # X1 + X2 + X3 + X1*X2 + X2*X3 + X3*X1 #' 非線形変換を含む式 (formula) の書き方 Y ~ f(X1) # f(X1) (fは任意の関数) Y ~ X1 + I(X2^2) # X1 + X2^2 ``` カテゴリカル変数の取り扱いには若干の注意が必要である. - 何も宣言しなくても通常は適切に対応してくれる. - 陽に扱う場合は関数 ~base::factor()~(順序を指定可能), ~base::as.factor()~(辞書順), ~forcats::as~factor()(出現順) を利用する. ## 因子属性の与え方の例 ```{r} X <- c("A", "S", "A", "B", "D") Y <- c(85, 100, 80, 70, 30) toy_data1 <- tibble(X, Y) toy_data2 <- toy_data1 |> # 因子化 mutate(X2 = factor(X)) # 関数as_factor()を用いてもよい glimpse(toy_data2) # 作成したデータフレームの素性を見る(pillar::glimpse()) toy_data3 <- toy_data2 |> # 順序付き(levels)の因子化 mutate(X3 = factor(X, levels=c("S","A","B","C","D"))) glimpse(toy_data3) # toy_data2とはfactorの順序が異なる toy_data4 <- toy_data2 |> mutate(Y2 = factor(Y > 60)) # 条件による因子化 glimpse(toy_data4) # 条件の真偽で2値に類別される ``` ## 問題 東京の気候データ(9-11月)を用いて,気温を回帰する以下のモデルを検討しなさい. - 日射量,気圧,湿度の線形回帰モデル - 湿度の対数を考えた線形回帰モデル - 最初のモデルにそれぞれの交互作用を加えたモデル 東京の気候データ(1年分)を用いて,気温を回帰する以下のモデルを検討しなさい. - 降水の有無を表すカテゴリカル変数を用いたモデル (雨が降ると気温が変化することを検証する) - 上記に月をカテゴリカル変数として加えたモデル (月毎の気温の差を考慮する) ## 解答欄 ```{r} #' ここにコードを書く ``` ## 解答例 9月から11月のデータを用いた分析を行うために,データを整理する. ```{r} #| code-fold: true tw_subset <- tw_data |> filter(month %in% 9:11) ``` 日射量,気圧,湿度を用いた回帰モデルの検討を行う.まず,解析の対象となる変数の散布図を描いておく. ```{r} #| code-fold: true tw_subset |> select(temp, solar, press, humid) |> ggpairs(columnLabels = c("気温","日射量","気圧","湿度")) ``` モデルを比較する. ```{r} #| code-fold: true #' 基本となる線形回帰モデル summary(lm(temp ~ solar + press + humid, data = tw_subset)) #' 湿度の対数を考えた線形回帰モデル summary(lm(temp ~ solar + press + log(humid), data = tw_subset)) #' 最初のモデルにそれぞれの交互作用を加えたモデル (書き方はいろいろある) summary(lm(temp ~ (solar + press + humid)^2, data = tw_subset)) #' 更に3つの変数の積を加えたモデル summary(lm(temp ~ solar * press * humid, data = tw_subset)) ``` 基本的なモデルと最後のモデルの視覚的な評価 (診断プロット) を行う. ```{r} #| code-fold: true autoplot(lm(temp ~ solar + press + humid, data = tw_subset)) autoplot(lm(temp ~ solar * press * humid, data = tw_subset)) ``` 雨の有無(カテゴリカル変数)と気温の関係を分析する. 雨の有無および月(整数値)をダミー化(因子化)[^1]する. [^1]: : 論理値は因子として扱われるが,明示的に因子としたい場合は `factor(rain > 0)` としても良い. 因子の属性を与える関数としては `base::as.factor()` と `forcats::as_factor()` があるが,前者は文字列を辞書順に並べ変えてから,後者はデータフレームでの出現順に因子化する.回帰の計算においては特に問題ないが,因子化の順序はグラフなど凡例の順番に影響する. ```{r} #| code-fold: true tw_data_fact <- tw_data |> mutate(rain = rain > 0, # 降雨の有無 month = as_factor(month)) # 月 ``` 通年でのモデルを検討する. ```{r} #| code-fold: true summary(lm(temp ~ rain, data = tw_data_fact)) ``` 通年では雨と気温の関係は積極的に支持されない. 月を説明変数として加えると月毎の気温の差を考慮した回帰式が推定される. ```{r} #| code-fold: true summary(lm(temp ~ rain + month, data = tw_data_fact)) ``` 月毎に比較した結果,雨の日の方が気温が低いことが示唆される. 診断プロットは以下のようになる. ```{r} #| code-fold: true autoplot(lm(temp ~ rain + month, data = tw_data_fact)) ``` # 参考 : その他の関数・パッケージ ## モデルの探索の例 変数が増えるとモデルの比較が困難となるが, 関数 `stats::step()` で自動化することができる. ```{r} adv_data <- read_csv('https://www.statlearning.com/s/Advertising.csv') summary(lm(sales ~ radio, data = adv_data)) summary(lm(sales ~ TV + radio, data = adv_data)) summary(lm(sales ~ TV + radio + newspaper, data = adv_data)) summary(adv_init <- lm(sales ~ TV * radio * newspaper, data = adv_data)) adv_opt <- step(adv_init) # 最大のモデルから削減増加による探索 summary(adv_opt) # 探索された(準)最適なモデルの確認 ``` 全探索ではないので最適とは限らないことに注意は必要である. ## 回帰分析のためのパッケージ 回帰分析のための様々な関数が `package::car` では提供されており,以下のことが可能である. - 回帰モデルの評価 - 与えられたデータの再現 - 新しいデータの予測 - モデルの再構築のための視覚化 - **residual plots**: 説明変数・予測値と残差の関係 - **marginal-model plots**: 説明変数と目的変数・モデルの関係 - **added-variable plots**: 説明変数・目的変数をその他の変数で回帰したときの残差の関係 - **component+residual plots**: 説明変数とそれ以外の説明変数による残差の関係 などが用意されている # 参考 : 人工データを用いた base R による予測の例 ## 人工データの生成例 以下はあくまで例なので,これを参考に自由に数値実験を設計して下さい. ```{r} #' モデル: y = -1 + 2 * x1 set.seed(1515) # 乱数のシード値(適宜変更せよ) n <- 50 # データ数の設定 x_obs <- tibble(x0 = 1, x1 = runif(n), # 説明変数1 x2 = runif(n)) # 説明変数2 beta <- c(-1, 2, 0) # (切片, x1の係数, x2の係数) 実質x2は使われていない sigma <- 1/2 # 誤差の標準偏差 epsilon <- rnorm(nrow(x_obs), sd = sigma) # 誤差項 toy_data <- x_obs |> # 目的変数の観測値を追加 mutate(y = as.vector(as.matrix(x_obs) %*% beta) + epsilon) ``` ## モデルの推定と評価の例 ```{r} toy_lm1 <- lm(y ~ x1, data = toy_data) # x1のみの正しいモデル toy_lm2 <- lm(y ~ x1 + x2, data = toy_data) # x1とx2の冗長なモデル toy_lm3 <- lm(y ~ x2, data = toy_data) # x2のみの誤ったモデル summary(toy_lm1) summary(toy_lm2) summary(toy_lm3) ``` ::: column-margin 表としてまとめるには,例えば以下のようにすればよい. ```{r} my_gts <- function(x){ tbl_regression(x, intercept = TRUE) |> add_significance_stars( pattern = "{estimate} ({conf.low}, {conf.high}){stars}", hide_ci = TRUE, hide_se = TRUE ) |> modify_header(estimate = "**Beta (95% CI)**") |> modify_footnote(estimate = "CI = Confidence Interval", abbreviation = TRUE) |> add_glance_table(include = c(r.squared, adj.r.squared, statistic, p.value)) } tbl_merge( tbls = list( my_gts(toy_lm1), my_gts(toy_lm2), my_gts(toy_lm3)), tab_spanner = c("モデル1","モデル2","モデル3")) |> modify_table_body( ~.x |> arrange(factor(variable, levels = c("(Intercept)","x1","x2")))) ``` ::: ## 予測の例 新規データを作成し,予測の仕組みを確認する. ```{r} x_new <- tibble(x0 = 1, x1 = runif(n), x2 = runif(n,-10,10)) #' 新規データに対する目的変数の真値 (誤差なし) y_tilde <- as.vector(as.matrix(x_new) %*% beta) #' 各モデルでの予測 y_hat1 <- predict(toy_lm1, newdata = x_new) # x_lm1による予測値 y_hat2 <- predict(toy_lm2, newdata = x_new) # x_lm2による予測値 y_hat3 <- predict(toy_lm3, newdata = x_new) # x_lm3による予測値 ``` ## 可視化の例 散布図を用いて,それぞれのモデルによる予測値と真値の関係を可視化する. ```{r} tibble(obs = y_tilde, model1 = y_hat1, model2 = y_hat2, model3 = y_hat3) |> pivot_longer(!obs) |> ggplot(aes(x = value, y = obs)) + geom_point(aes(colour = name)) + geom_abline(slope = 1, intercept = 0, colour = "gray") + labs(x = "fitted value", y = "observed value") + theme(legend.title = element_blank()) ``` 正しいモデルでは正確に予測が行われているが,不要な項が含まれると予測誤差が大きくなることがわかる. ## 数値的な評価の例 雑音のないデータ $\tilde{y}$ と予測値の相関係数(2乗すると決定係数に相当)を用いて数値的な評価をする. ```{r} cor(y_tilde, y_hat1)^2 cor(y_tilde, y_hat2)^2 cor(y_tilde, y_hat3)^2 ```