回帰分析では、ある説明変数が結果に与える影響が、別の変数の値によって変わることがあります。このような「効果の修飾(effect modification)」をモデルに組み込むのが**交互作用項(interaction term)**です。 ここでは、小学生の**身長(cm)**を結果変数、**学年**と**性別**を説明変数とする例で考えます。男子は1年生の時点で女子より身長が高く、さらに学年が上がるにつれて男女差が広がっていきます。つまり、「学年が上がることによる身長の増加量」が性別によって異なります。これが交互作用です。 ## サンプルデータ 以下は架空の小学生データ(各学年・男女1名ずつ、計12名)です。「male」という変数を用意し、男子 = 1、女子 = 0 とコーディングしています。 | ID | 性別 | male | 学年 | 身長 (cm) | |----|------|------|------|-----------| | 1 | 女子 | 0 | 1 | 115 | | 2 | 女子 | 0 | 2 | 120 | | 3 | 女子 | 0 | 3 | 126 | | 4 | 女子 | 0 | 4 | 130 | | 5 | 女子 | 0 | 5 | 135 | | 6 | 女子 | 0 | 6 | 141 | | 7 | 男子 | 1 | 1 | 121 | | 8 | 男子 | 1 | 2 | 129 | | 9 | 男子 | 1 | 3 | 135 | | 10 | 男子 | 1 | 4 | 142 | | 11 | 男子 | 1 | 5 | 148 | | 12 | 男子 | 1 | 6 | 156 | 女子は約 5 cm/年のペースで伸びています。男子は1年生の時点で女子より 6 cm 高く、伸び率も約 7 cm/年と大きいため、学年が上がるほど差が開いていきます。 ## モデルの構築:3段階で理解する ### モデル1:学年のみ(単回帰) $ \text{身長}_i = \beta_0 + \beta_1 \cdot \text{学年}_i + \varepsilon_i $ 最も単純なモデルで、性別を無視しています。学年が1つ上がるごとに身長が $\beta_1$ cm 増えると仮定しており、男女の違いは考慮されません。 ```r m1 <- lm(height ~ grade, data = d) summary(m1) ``` ``` Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 112.267 4.002 28.051 7.7e-11 *** grade 5.971 1.028 5.811 0.00017 *** ``` ### モデル2:学年+性別(主効果のみ) $ \text{身長}_i = \beta_0 + \beta_1 \cdot \text{学年}_i + \beta_2 \cdot \text{male}_i + \varepsilon_i $ このモデルでは性別による切片の違いを許しています。式を性別ごとに書き下してみます。 **女子(male = 0)のとき:** $ \text{身長} = \beta_0 + \beta_1 \cdot \text{学年} $ **男子(male = 1)のとき:** $ \text{身長} = (\beta_0 + \beta_2) + \beta_1 \cdot \text{学年} $ > 2本の回帰直線は**平行**になります。傾き $\beta_1$(学年あたりの身長増加量)は男女共通で、切片だけが $\beta_2$ だけずれます。つまり「男子は女子よりどの学年でも一律に $\beta_2$ cm 高い」という仮定を置いています。 ```r m2 <- lm(height ~ grade + male, data = d) summary(m2) ``` ``` Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 106.9333 1.2747 83.89 2.46e-14 *** grade 5.9714 0.2998 19.92 9.40e-09 *** male 10.6667 1.0239 10.42 2.54e-06 *** ``` しかし現実には、学年が上がるにつれて男女差が広がっていきます。これは「傾き」自体が男女で異なることを意味しており、平行な直線では表現できません。 ### モデル3:交互作用あり $ \text{身長}_i = \beta_0 + \beta_1 \cdot \text{学年}_i + \beta_2 \cdot \text{male}_i + \beta_3 \cdot (\text{学年}_i \times \text{male}_i) + \varepsilon_i $ ここで $\beta_3 \cdot (\text{学年}_i \times \text{male}_i)$ が**交互作用項**です。R では `*` 演算子で主効果と交互作用を同時に投入できます。 ```r m3 <- lm(height ~ grade * male, data = d) summary(m3) ``` ``` Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 109.9333 0.5297 207.519 3.25e-16 *** grade 5.1143 0.1360 37.598 2.75e-10 *** male 4.6667 0.7492 6.229 0.000251 *** grade:male 1.7143 0.1924 8.911 1.99e-05 *** ``` `grade * male` は `grade + male + grade:male` と同じ意味です。`grade:male` が交互作用項にあたります。 ![[interaction_models_r.png]] モデル2では2本の直線が平行でしたが、モデル3では**切片も傾きも異なる**2本の直線になっています。 ## 交互作用項の式変形 モデル3を変形し、各係数の意味を明らかにします。 ### 学年でくくる $ \begin{aligned} \text{身長} &= \beta_0 + \beta_1 \cdot \text{学年} + \beta_2 \cdot \text{male} + \beta_3 \cdot \text{male} \cdot \text{学年} \\[6pt] &= \beta_0 + \beta_2 \cdot \text{male} + (\beta_1 + \beta_3 \cdot \text{male}) \cdot \text{学年} \end{aligned} $ この形にすると構造が見やすくなります。 - **切片**:$\beta_0 + \beta_2 \cdot \text{male}$ → male の値(0か1)によって切片が変わる - **学年の傾き**:$(\beta_1 + \beta_3 \cdot \text{male})$ → male の値によって傾きが変わる つまり $\beta_3$ は「male = 1 になったときに傾きがどれだけ変化するか」を直接表しています。 ### 並べて比較する | | 切片 | 傾き(学年の係数) | |------------------------|-------------------|---------------------------| | **女子**(male = 0) | $\beta_0$ | $\beta_1$ | | **男子**(male = 1) | $\beta_0 + \beta_2$ | $\beta_1 + \beta_3$ | | **差(男子 − 女子)** | $\beta_2$ | $\beta_3$ | 各係数の意味をまとめます。 - $\beta_0 = 109.93$:女子で学年 = 0(仮想的な入学前)のときの期待身長 - $\beta_1 = 5.11$:**女子における**学年が1つ上がるごとの身長の増加量(cm/学年) - $\beta_2 = 4.67$:学年 = 0 における男女の身長差(**切片の差**) - $\beta_3 = 1.71$:学年あたりの身長増加量が、男子では女子と比べてどれだけ大きいか(**傾きの差**) ## 数値例 モデル3の推定結果を当てはめます。 $ \hat{\text{身長}} = 109.93 + 5.11 \cdot \text{学年} + 4.67 \cdot \text{male} + 1.71 \cdot (\text{学年} \times \text{male}) $ **女子の回帰式:** $ \hat{\text{身長}} = 109.93 + 5.11 \cdot \text{学年} $ **男子の回帰式:** $ \hat{\text{身長}} = (109.93 + 4.67) + (5.11 + 1.71) \cdot \text{学年} = 114.60 + 6.83 \cdot \text{学年} $ ```r b <- coef(m3) cat(sprintf("女子: 身長 = %.2f + %.4f * 学年\n", b["(Intercept)"], b["grade"])) cat(sprintf("男子: 身長 = %.2f + %.4f * 学年\n", b["(Intercept)"] + b["male"], b["grade"] + b["grade:male"])) ``` ``` 女子: 身長 = 109.93 + 5.1143 * 学年 男子: 身長 = 114.60 + 6.8286 * 学年 ``` ### 男女差の変化を確認する | 学年 | 女子の予測身長 | 男子の予測身長 | 差(男子 − 女子) | |------|---------------|---------------|-------------------| | 1 | 115.0 | 121.4 | 6.4 | | 2 | 120.2 | 128.3 | 8.1 | | 3 | 125.3 | 135.1 | 9.8 | | 4 | 130.4 | 141.9 | 11.5 | | 5 | 135.5 | 148.7 | 13.2 | | 6 | 140.6 | 155.6 | 15.0 | - 女子:傾き 5.11 cm/学年 - 男子:傾き 6.83 cm/学年 - 1学年あたりの傾きの差:1.71 cm(= $\beta_3$, p < 0.001) 1年生の時点で約 6 cm の差がありますが、6年生では約 15 cm に広がります。$\beta_2$ が切片の差、$\beta_3$ が傾きの差を捉えています。 ## 交互作用項の検定 交互作用が統計的に意味があるかどうかは、$\beta_3 = 0$ という帰無仮説を検定します。 $ H_0: \beta_3 = 0 \quad \text{(学年あたりの身長増加量は男女で同じ)} $ $ H_1: \beta_3 \neq 0 \quad \text{(学年あたりの身長増加量は男女で異なる)} $ $\beta_3 = 0$ が棄却されなければ、交互作用項を除いたモデル2(平行な直線)で十分ということになります。棄却されれば、身長の伸び方が男女で違うと結論づけられます。今回の結果では p < 0.001 で有意でした。 ## まとめ 1. **交互作用項**は「ある変数の効果が別の変数の水準によって異なる」ことをモデルに取り込む手段です。 2. カテゴリ × 連続の交互作用では、**グループごとに切片と傾きが異なる回帰直線**が得られます。 3. 交互作用項の係数 $\beta_3$ は「傾きの差」を表しており、これが 0 かどうかを検定することで交互作用の有無を判断できます。 4. 交互作用項がある場合、主効果の係数は「もう一方の変数が 0 のときの効果」を意味するため、解釈に注意が必要です。 ## 完全動作コード 以下を R コンソールまたはスクリプトに貼り付けてそのまま実行できます。 ```r # ============================================ # 交互作用項の理解:小学生の身長データ # ============================================ # --- データ入力 --- d <- data.frame( sex = rep(c("女子", "男子"), each = 6), male = rep(c(0, 1), each = 6), grade = rep(1:6, 2), height = c(115, 120, 126, 130, 135, 141, 121, 129, 135, 142, 148, 156) ) print(d) # --- モデル1: 学年のみ --- cat("\n===== モデル1: 学年のみ =====\n") m1 <- lm(height ~ grade, data = d) summary(m1) # --- モデル2: 学年 + 性別(主効果のみ) --- cat("\n===== モデル2: 学年 + 性別(平行線) =====\n") m2 <- lm(height ~ grade + male, data = d) summary(m2) # --- モデル3: 交互作用あり --- cat("\n===== モデル3: 交互作用あり =====\n") m3 <- lm(height ~ grade * male, data = d) summary(m3) # --- 各グループの回帰式を表示 --- b <- coef(m3) cat(sprintf("\n女子: 身長 = %.2f + %.4f * 学年\n", b["(Intercept)"], b["grade"])) cat(sprintf("男子: 身長 = %.2f + %.4f * 学年\n", b["(Intercept)"] + b["male"], b["grade"] + b["grade:male"])) # --- グラフ: モデル2(平行線)vs モデル3(異なる傾き) --- par(mfrow = c(1, 2), mar = c(4, 4, 3, 1)) cols <- c("red", "blue") # モデル2 plot(d$grade, d$height, type = "n", xlab = "Grade", ylab = "Height (cm)", main = "Model 2: Main effects only", xlim = c(1, 6), ylim = c(110, 160)) for (i in 0:1) { sub <- d[d$male == i, ] points(sub$grade, sub$height, col = cols[i + 1], pch = 16) abline(a = coef(m2)["(Intercept)"] + coef(m2)["male"] * i, b = coef(m2)["grade"], col = cols[i + 1], lwd = 2) } legend("topleft", legend = c("Female", "Male"), col = cols, pch = 16, lwd = 2, bty = "n") # モデル3 plot(d$grade, d$height, type = "n", xlab = "Grade", ylab = "Height (cm)", main = "Model 3: With interaction", xlim = c(1, 6), ylim = c(110, 160)) for (i in 0:1) { sub <- d[d$male == i, ] points(sub$grade, sub$height, col = cols[i + 1], pch = 16) abline(a = b["(Intercept)"] + b["male"] * i, b = b["grade"] + b["grade:male"] * i, col = cols[i + 1], lwd = 2) } legend("topleft", legend = c("Female", "Male"), col = cols, pch = 16, lwd = 2, bty = "n") ```