回帰分析では、ある説明変数が結果に与える影響が、別の変数の値によって変わることがあります。このような「効果の修飾(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")
```