政策分析の量的基礎(入門):第3回 回帰分析
授業計画
※太字はこのレジュメの該当回
授業概要の説明
リサーチデザイン・データ収集の方法
Rのインストール・記述統計の出し方
二変数の関係(クロス表・散布図)
統計的検定
6. 回帰分析(1)
7. 回帰分析(2)
8. 回帰分析(3)
9. パネルデータ分析
10. 計量分析を用いた文献を読む
最終発表に関する実習・相談
学生による分析結果発表(1)
学生による分析結果発表(2)
学生による分析結果発表(3)
フィードバック
I.単回帰分析
1.単回帰分析とは?
単回帰分析の目的:1つの従属変数を独立変数に回帰する方法 (regress y on x)。従属変数Yの分散が、独立変数X によってどれだけ説明されているのかを推定する
研究の設計
使用するデータ:福祉国家に関するOECD諸国のデータ
- 研究の問い:社会保障費を規定する要因は何か?
- 検証仮説:高齢者の人口割合が増加することで、社会保障費は増加する。
- 利用するデータ:OECD諸国の比較福祉国家データ(Comparative Welfare State Dataset)データの入手先はこちら
- 変数の設定
- 独立変数:高齢者の人口割合
- 従属変数:社会保障支出の対GDP比
データフレームと変数の設定
まずはいつものように、データを読み込む。
次に、分析に必要と思われる変数を選択し、データフレームを構築しておこう。そして、head()
コマンドによって、データフレームが意図したように構築できたかを確認する。
df_ex<- df_cws %>% dplyr::select(year,id,socx_pub, pop,po65,postgini_2559,lisrpr_tot,
hunemr,leftseat,leftvot,rtseat,rtvot,vturn, pres)
head(df_ex)
## year id socx_pub pop po65 postgini_2559 lisrpr_tot hunemr leftseat
## 1 1960 AUL NA 10275.0 874.9 NA NA NA 36.9
## 2 1961 AUL NA 10508.2 894.6 NA NA NA 37.9
## 3 1962 AUL NA 10700.5 913.6 NA NA NA 49.2
## 4 1963 AUL NA 10906.9 933.0 NA NA NA 48.5
## 5 1964 AUL NA 11121.6 948.1 NA NA NA 41.0
## 6 1965 AUL NA 11340.9 966.3 NA NA NA 41.0
## leftvot rtseat rtvot vturn pres
## 1 42.8 63.1 46.5 95.48 0
## 2 43.2 62.1 46.1 95.46 0
## 3 47.9 50.8 42.1 95.27 0
## 4 47.7 51.5 42.4 95.31 0
## 5 45.5 59.0 46.0 95.73 0
## 6 45.5 59.0 46.0 95.73 0
必要な変数の記述統計を確かめる。
## vars n mean sd median trimmed mad min
## year 1 1210 1987.00 15.88 1987.00 1987.00 20.76 1960.00
## id* 2 1210 11.50 6.35 11.50 11.50 8.15 1.00
## socx_pub 3 651 20.97 5.14 20.71 20.97 5.68 9.90
## pop 4 1144 34795.58 54515.97 10133.05 21884.97 9536.45 314.90
## po65 5 1138 4534.67 6982.47 1434.06 2903.01 1423.69 33.90
## postgini_2559 6 123 0.28 0.05 0.28 0.28 0.05 0.18
## lisrpr_tot 7 137 9.90 3.77 9.12 9.71 4.32 3.73
## hunemr 8 670 6.74 3.63 6.37 6.49 3.20 0.00
## leftseat 9 1105 37.17 15.65 40.80 38.65 12.45 0.00
## leftvot 10 1105 37.06 13.42 40.40 38.74 10.08 0.00
## rtseat 11 1105 32.14 19.19 32.60 32.17 21.20 0.00
## rtvot 12 1105 29.82 15.96 32.60 30.69 18.24 0.00
## vturn 13 1105 78.58 12.13 80.42 79.71 12.50 42.25
## pres 14 1102 0.19 0.39 0.00 0.11 0.00 0.00
## max range skew kurtosis se
## year 2014.00 54.00 0.00 -1.20 0.46
## id* 22.00 21.00 0.00 -1.21 0.18
## socx_pub 35.68 25.78 0.07 -0.58 0.20
## pop 311591.90 311277.00 2.91 9.10 1611.80
## po65 41394.14 41360.24 2.72 8.02 206.98
## postgini_2559 0.36 0.18 -0.15 -0.94 0.00
## lisrpr_tot 17.94 14.21 0.33 -0.98 0.32
## hunemr 21.64 21.64 0.90 1.59 0.14
## leftseat 68.60 68.60 -0.81 0.00 0.47
## leftvot 60.70 60.70 -1.13 0.86 0.40
## rtseat 85.10 85.10 0.00 -0.77 0.58
## rtvot 60.70 60.70 -0.38 -0.96 0.48
## vturn 95.77 53.52 -0.76 0.05 0.36
## pres 1.00 1.00 1.59 0.52 0.01
2.散布図から見る最小二乗法(Ordinary Least Square: OLS)
データの可視化を通して、最小二乗法(OLS)の基盤を理解する。まず、社会保障支出の分布をみると・・・
df_ex %>%
ggplot(aes(x = socx_pub)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "grey", color = "black") +
geom_density(lwd = 2) +
labs(title = "社会保障費の対GDP比のヒストグラム",
x = "値",
y = "密度") +
theme_bw()
正規分布にかなりの程度即した形状になっているようである。これに対して、高齢者人口は・・・
df_ex %>%
ggplot(aes(x = po65)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "grey", color = "black") +
geom_density(lwd = 2) +
labs(title = "高齢者人口のヒストグラム",
x = "値",
y = "密度") +
theme_bw()
分布が偏っていることが明らか。後に見る残差の分布の観点から、分析に利用する変数の分布に著しい偏りがあることは望ましくない。よって変数を割合値に変換することで調整。 独立変数である高齢者の対全人口比を算出し、データに組み込む。
調整後の値を改めてヒストグラムで確認すると、分布の偏りが解消され、以下の推定に適した形状に変換されていることがわかる。
df_ex %>%
ggplot(aes(x = peraged)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "grey", color = "black") +
geom_density(lwd = 2) +
labs(title = "高齢者人口の対全人口比のヒストグラム",
x = "値",
y = "密度") +
theme_bw()
次に、高齢者の人口割合と社会保障支出の関係性を散布図によって確認しよう。
library(ggExtra)
#------- 基本の散布図を作成
g <- df_ex %>%
ggplot( aes(x = peraged, y = socx_pub)) +
geom_point(alpha = 0.6) +
xlab("65%人口割合") +
ylab("社会保障費の対GDP比") +
ggtitle("高齢者人口と社会保障費の関係") +
theme_bw()
#------- ggMarginal() で散布図の横(x)と縦(y)にヒストグラムを追加
g1 <- ggMarginal(g, type = "histogram", bins = 8, fill = "lightblue", color = "black")
plot(g1)
散布図を見ると、両者の間には何らかの直線で表現できそうな、線形(linear)の関係があることが示唆される。ここに回帰直線を引いて確かめる。
g2 <- df_ex %>%
ggplot(aes(x = peraged, y = socx_pub)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE, color = "red") + # 回帰直線を描画
xlab("65%人口割合") +
ylab("社会保障費の対GDP比") +
ggtitle("高齢者人口と社会保障費の関係(回帰直線を併記)") +
theme_bw()
plot(g2)
この回帰直線は、次式で表現できる。
\(y_{welfare}=\beta_{0}+\beta_{1}x_{aged}+\epsilon_{t}.\)
\(y_{welfare}\)において、右下の字は小さく書かれている。これは、「この式におけるyはwelfareを意味する」と表していて添え字という。添え字も含めて、各項の意味(notation)を以下で説明する。
\(y_{welfare}\): 従属変数でwelfareを表す
\(\beta_0\): 定数項。皆が知っている表現でいえばy切片に当たる
\(\beta_1\): 回帰係数。ここでは\(x_{aged}\)の回帰係数である。皆が知っている表現でいえば直線の傾きに当たる
\(x_{aged}\): 独立変数で高齢者人口比を表す
\(\epsilon_{t}\): 誤差項
上記式を、図内に描画してみよう。何の直線なのかを明示できると読者もわかりやすい。
x_pos <- 0.85 * max(df_ex$peraged, na.rm = TRUE) #回帰直線を示すX位置とY位置を指定している。各変数の最大値の85%と95%の位置(つまりかなり右上)に描画すると指示
y_pos <- 0.95 * max(df_ex$socx_pub, na.rm = TRUE)
g3 <- df_ex %>%
ggplot(aes(x = peraged, y = socx_pub)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE, color = "red") +
xlab("65%人口割合") +
ylab("社会保障費の対GDP比") +
ggtitle("高齢者人口と社会保障費の関係(推定モデルを併記)") +
annotate( #geom_text()と類似して文字を図内に含める関数
"text",
x = x_pos,
y = y_pos,
label = "y[welfare] == beta[0] + beta[1]*x[aged] + epsilon[t]",
parse = TRUE, #parse = Tとして上記の記法が反映されるようにしている
size = 3,
color = "black"
) +
theme_bw()
plot(g3)
この回帰直線をもとに、全変動、回帰変動、残差変動とは何であるかを確認する。
\(i\)番目の観察\(y_i\)の残差は、 \[|y_{i}-\beta_{0}-\beta_{1}x_{i}|\] こうした個々の残差の平方和、
\[\sum^{n}_{i=1}(y_{i}-\beta_{0}-\beta_{1}x_{i})^2\] を最小化するのが最小二乗法。
g4 <- df_ex %>%
ggplot(aes(x = peraged, y = socx_pub)) +
geom_point(size = 3, alpha = 0.2, colour = "black") +
geom_point(size = 2, alpha = 0.2, colour = "red") +
geom_smooth(method = "lm") + #geom_smooth(method = "lm", se = FALSE)のうち「se = FALSE」を消し(se = TRUEにし)、誤差を描いている
xlab("65%人口割合") +
ylab("社会保障費の対GDP比") +
ggtitle("高齢者人口と社会保障費の関係()") +
annotate( #geom_text()と類似して文字を図内に含める関数
"text",
x = x_pos,
y = y_pos,
label = "y[welfare] == beta[0] + beta[1]*x[aged] + epsilon[t]",
parse = TRUE, #parse = Tとして上記の記法が反映されるようにしている
size = 3,
color = "black"
) +
theme_bw()
plot(g4)
先ほどの回帰直線に加えて、バンドが出現しているのが分かる。このバンドが信頼区間(confidence intervals)にあたる。信頼区間は、標準誤差から計算できる。
WORK
手元にあるCodebookから、社会保障費の対GDP割合に影響を与えると思う変数を探し、仮説を立てたうえで、散布図を描画しよう。複数の変数をピックアップして、複数の散布図を描いてもらうことが望ましい。
得られた図を検討しよう。近くの人に見せて、意見をもらおう。
次に知りたいことは、この回帰直線は、どのように導かれるものかである。回帰直線を導く方法、計算はどのような手順になっているだろうか?
3. 最小二乗法(Ordinary Least Square: OLS)による単回帰モデルの実践
OLS推定の実践
ここまでの散布図も検討も踏まえて、高齢者人口割合で、社会保障支出を説明する単回帰モデルを実行する。
推定するモデルは、次のように表記される;
\(y_{welfare}=\beta_{0}+\beta_{1}x_{aged}+e_{i}.\)
これを行列形式で表記すると、以下のように書き直すことができる。
\(Y_{i}=X_{i}\beta+e.\)
この行列形式をもとにすると、OLSは、
\(\sum^n_{i=1}(Y_{i}-X_{i}\beta)^2\)
を最小化するものとして定義できる。これを解くと\(\beta_{1}\)と\(\beta_{0}\)は、
\(\hat{\beta}_{1}=Cor(Y,X)\frac{sd(Y)}{sd(X)},\)
\(\hat{\beta}_{0}=\hat{Y}-\hat{\beta}_{1}\hat{X},\)
として計算できる。この\(\hat{\beta}_{1}\)と\(\hat{\beta}_{0}\)を計算するためにR
では、次のコマンドが利用されるが・・・
##
## Call:
## lm(formula = socx_pub ~ peraged, data = na.omit(df_ex))
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.9707 -3.5175 -0.3949 3.0415 9.0872
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.624 2.592 0.627 0.532
## peraged 141.266 17.675 7.992 3.97e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.945 on 91 degrees of freedom
## Multiple R-squared: 0.4124, Adjusted R-squared: 0.406
## F-statistic: 63.88 on 1 and 91 DF, p-value: 3.97e-12
上記の推定結果を、先ほどの散布図に反映しておく。
# 回帰モデルを推定:定数項と係数の値を抽出
reg_mod <- lm(socx_pub ~ peraged, data = na.omit(df_ex))
int_val <- coef(reg_mod)[1]
slope_val <- coef(reg_mod)[2]
# 注釈テキストを生成 (R の expression 形式に合わせる)
# round() により小数点以下3桁に丸めた値を表示するように指示
label_expr <- paste0("y[welfare] == ", round(int_val, 3), " + ", round(slope_val, 3), "*x[aged] + epsilon[t]")
# x_pos, y_pos は注釈の表示位置
x_pos <- 0.9 * max(df_ex$peraged, na.rm = TRUE)
y_pos <- 0.9 * max(df_ex$socx_pub, na.rm = TRUE)
# 散布図と回帰直線の作成
g5 <- df_ex %>%
ggplot(aes(x = peraged, y = socx_pub)) +
geom_point(size = 3, alpha = 0.2, colour = "black") +
geom_point(size = 2, alpha = 0.2, colour = "red") +
geom_smooth(method = "lm", se = TRUE) +
xlab("65%人口割合") +
ylab("社会保障費の対GDP比") +
ggtitle("高齢者人口と社会保障費の関係") +
annotate(
"text",
x = x_pos,
y = y_pos,
label = label_expr, #先ほどの数式を書き入れると指示
parse = TRUE,
size = 3,
color = "black"
) +
theme_bw()
plot(g5)
この結果と整合的なものが、上記の推定モデルの背景となる数式をもとに計算した場合にも得られるか確認しておこう。
results1 <- df_ex %>%
# 欠損値を除いたデータ処理
na.omit() %>%
{
# 手計算による傾きと切片の計算
beta1_manual <- cor(.$socx_pub, .$peraged, use = "complete.obs") *
(sd(.$socx_pub, na.rm = TRUE) / sd(.$peraged, na.rm = TRUE))
beta0_manual <- mean(.$socx_pub, na.rm = TRUE) - beta1_manual * mean(.$peraged, na.rm = TRUE)
manual_vec <- c(beta0_manual, beta1_manual)
# lm() を用いた線形回帰モデルの推定結果を再度計算
lm_vec <- lm(socx_pub ~ peraged, data = .) %>% coef()
# 手計算結果と lm() の結果を行ごとに結合
rbind("manual" = manual_vec, "lm(df_ex$socx_pub ~ df_ex$peraged)" = lm_vec)
} %>%
{
# 列名の設定
colnames(.) <- c("切片(定数項)", "傾き(係数)")
.
}
results1
## 切片(定数項) 傾き(係数)
## manual 1.624476 141.2655
## lm(df_ex$socx_pub ~ df_ex$peraged) 1.624476 141.2655
では、分析結果を解釈していく。それぞれの項目が何を意味するか、下図を参考に検討する。
- 赤四角:係数
- 青四角:標準誤差
- 緑四角:\(t\)値 (\(t\)検定の\(t\)統計量)
- 黄四角:\(p\)値 (\(t\)検定の有意確率値)
推定量を解釈する上でのポイントは以下の通りである。
各変数についての\(t\)検定の結果はどうか?
各係数の符号は、仮説にける符号条件を満たしているか?
各係数の値の大きさは、実質的に意味のあるものか?
次に解釈する項目は、モデルの妥当性についてである。
グレー四角:決定係数(\(R^2\))
ピンク四角:\(F\)検定の結果
WORK
散布図でも行った、独自の仮説にもとづく独立変数について、実際にOLS推定を実行してほしい。可能なら、複数のモデルを試してみよう。
解釈を行おう。可能であれば、近くの人と分析結果を紹介し、説明したり、議論したりしてほしい。
中心化(centering)
分析結果の意味について考えてみよう。まずは定数項に注目してみほしい。
定数項の意味:独立変数が0の時に予測されるYの値
→独立変数が0の時に意味があるのか?高齢者人口割合が0になる、という状態が存在するのか?
【中心化(centering)】:データの原点から平均値を差し引いた値をもとに各パラメターを算出
\(Y-\bar{Y}=\beta_{1}(X-\bar{X})+e.\)
results2 <- df_ex %>%
na.omit() %>%
mutate(
centered_socx_pub = socx_pub - mean(socx_pub, na.rm = TRUE),# 従属変数の中心化
centered_peraged =peraged - mean(peraged, na.rm = TRUE)# 独立変数の中心化
) %>%
{
orig_cor <- cor(.$socx_pub, .$peraged, use = "complete.obs")
centered_cor <- cor(.$centered_socx_pub, .$centered_peraged, use = "complete.obs")
lm_model <- lm(centered_socx_pub ~ centered_peraged, data = .)
slope <- coef(lm_model)[2]
intercept <- coef(lm_model)[1]
tibble(
統計量 = c("相関係数(社保, 高齢)", "相関係数(中心化・社保,中心化・高齢)", "中心化後の係数", "中心化後の定数項"),
推定結果 = round(c(orig_cor, centered_cor, slope, intercept), 3)
)# 元の相関と、中心化後の相関および回帰モデルの係数をまとめる
}
results2
## # A tibble: 4 × 2
## 統計量 推定結果
## <chr> <dbl>
## 1 相関係数(社保, 高齢) 0.642
## 2 相関係数(中心化・社保,中心化・高齢) 0.642
## 3 中心化後の係数 141.
## 4 中心化後の定数項 0
g6 <- df_ex %>%
# 中心化:socx_pub と peraged の各値から平均を引く、中心化済みの値を利用
mutate(
centered_socx_pub = socx_pub - mean(socx_pub, na.rm = TRUE),
centered_peraged = peraged - mean(peraged, na.rm = TRUE)
) %>%
ggplot(aes(x = centered_peraged, y = centered_socx_pub)) +
geom_point(size = 3, alpha = 0.2, colour = "black") +
geom_point(size = 2, alpha = 0.2, colour = "red") +
geom_smooth(method = "lm") +
labs(
x = "中心化された65%人口割合",
y = "中心化された社会保障費の対GDP比",
title = "中心化後の高齢者人口と社会保障費の関係"
) +
theme_bw()
g6
上記の結果において、係数(傾き)は変わらず、定数項(切片)の値のみが変化していることに注目しよう。
正規化(normalize)
【正規化(normalize)】:特徴量の値の範囲を一定に抑える処理
results3 <- df_ex %>%
# 正規化:平均を引き、標準偏差で割る
mutate(
norm_socx_pub = (socx_pub - mean(socx_pub, na.rm = TRUE)) / sd(socx_pub, na.rm = TRUE),# 従属変数の正規化
norm_peraged = (peraged - mean(peraged, na.rm = TRUE)) / sd(peraged, na.rm = TRUE)# 独立変数の正規化
) %>%
{
orig_cor <- cor(.$socx_pub, .$peraged, use = "complete.obs")
norm_cor <- cor(.$norm_socx_pub, .$norm_peraged, use = "complete.obs")
lm_model <- lm(norm_socx_pub ~ norm_peraged, data = .)
slope <- coef(lm_model)[2]
intercept <- coef(lm_model)[1]
tibble(
統計量 = c("相関係数(社保, 高齢)", "相関係数(正規化・社保,正規化・高齢)", "正規化後の係数", "正規化後の定数項"),
推定結果 = round(c(orig_cor, norm_cor, slope, intercept), 3)
)# 元の相関と、正規化後の相関および回帰モデルの係数をまとめる
}
results3
## # A tibble: 4 × 2
## 統計量 推定結果
## <chr> <dbl>
## 1 相関係数(社保, 高齢) 0.585
## 2 相関係数(正規化・社保,正規化・高齢) 0.585
## 3 正規化後の係数 0.721
## 4 正規化後の定数項 -0.296
正規化後の係数・定数項が変化していることがわかるはず。
残差(residuals)の検討
次に算出された残差(residuals)について検討する。以下の残差と予測値の計算を示す。
library(gridExtra) #複数の図を1枚に図にアレンジするためのパッケージ
#------- データ前処理
# df_exから欠損値を除去し、単回帰分析を実施して予測値と残差を計算
df_ex_clean <- df_ex %>%
na.omit() %>%
mutate(
pred = predict(lm(socx_pub ~ peraged, data = .)),
resid = resid(lm(socx_pub ~ peraged, data = .))
)
#------- 第1パネル:実測値&回帰直線に対する残差プロット
p1 <- ggplot(df_ex_clean, aes(x = peraged, y = socx_pub)) +
geom_point(size = 3, alpha = 0.2, shape = 21, fill = "lightblue", color = "black") +
geom_point(size = 2, alpha = 0.2, color = "red") +
# 回帰直線を追加(信頼区間は非表示)
geom_smooth(method = "lm", se = FALSE, color = "black", size = 1.2) +
# 各観測点から予測値 (pred) への垂直赤線を追加
geom_segment(aes(x = peraged, xend = peraged, y = socx_pub, yend = pred),
color = "red", size = 1.2) +
labs(x = "X: 高齢者人口割合", y = "Y: 社会保障費の対GDP比",
title = "回帰直線に対する残差") +
theme_bw()
#------- 第2パネル:残差プロット
p2 <- ggplot(df_ex_clean, aes(x = peraged, y = resid)) +
geom_point(size = 3, alpha = 0.2, shape = 21, fill = "lightblue", color = "black") +
geom_point(size = 2, alpha = 0.2, color = "red") +
# y=0 の水平線(参考線)を点線で描画
geom_hline(yintercept = 0, linetype = "dashed", size = 1.2) +
# 各観測点の残差から0へ向かう垂直赤線を描画
geom_segment(aes(x = peraged, xend = peraged, y = resid, yend = 0),
color = "red", size = 1.2) +
labs(x = "X: 高齢者人口割合", y = "残差", title = "残差 vs X") +
theme_bw()
# --- プロットの配置 ---
# grid.arrange() を用いて2つのプロットを横並び (1行2列) に配置
grid.arrange(p1, p2, ncol = 2)
このように見ると、残差がそれぞれのXに対して不均一に分散しているように見える・・・これは何を意味しているのか、どのような問題があるのかについては、のちほど詳しく検討しよう。
- 自身で行った推定モデルについても、残差の確認を行う。どのようなことが読み取れるか。
Goodness of fitを考える
「モデルの当てはまり(Goodness of fit)」についても検討しよう。
- モデルの当てはまりを測る指標:\(R^2\)(アール・スクエア)
\(R^2=\frac{\sum^n_i=1(\hat{Y}-\bar{Y})^2}{\sum^n_{i=1}(Y_{i}-\bar{Y})^2}\)
\(R^2=1-\frac{\sum^n_{i=1}\hat{\mu}_i^2}{\sum^n_{i=1}(Y_{i}-\bar{Y})^2}\)
\(\sum^n_{i=1}(Y_{i}-\bar{Y})^2\):\(Y_i\)の全変動
\(\sum^n_{i=1}(\hat{Y}_{i}-\bar{Y})^2\):\(\hat{Y}_i\)で説明される部分。回帰直線によって説明される部分。
\(\sum^n_{i=1}\hat{\mu}_i^2\):\(\hat{Y}_i\)で説明されない部分。回帰直線によって説明されない部分
更に確認しておこう。\(y\)と\(x\)の相関係数\(r\)は・・・
## [1] 0.6422189
相関係数\(r\)の2乗値は・・・
## [1] 0.4124451
相関係数\(r\)の2乗値は\(R^2\)に一致する。
- \(R^2\)の評価:一概にどの値であればいいとは言い難い。いま単回帰分析で、「0.412」という値であれば、十分なモデルの当てはまりであると判断できる
4. 分析結果をどう見せるのか?
上記のように、分析結果をただ画面で見せられても、読者は困惑してしまう。他授業でも見ているように、分析結果は図表にして示さなくてはならない。今のところ行っているのは、単回帰のみなので、まずは表の出力について説明する。
分析結果を、いちいちエクセルなどに転記していてはミスも起こるし、非効率である。回帰分析の結果を、一括して表に変換してくれるパッケージとして、\(\texttt{stargazer}\)がある。\(\texttt{stargazer}\)を利用して、効率的に表を作成しよう。
以下は、Rのコンソール上で表をきれいに出力し、推定結果を確かめるのに役立つ。各自、Rの画面に貼り付けて実行してみほしい。
library(stargazer)
# 単回帰モデルの推定(欠損値は除外)
result_ols <- lm(socx_pub ~ peraged, data = na.omit(df_ex))
# stargazer を利用して結果をテキスト形式で表示
stargazer(result_ols,
type = "text",
title = "単回帰分析結果",
dep.var.labels = "社会保障費の対GDP比",
covariate.labels = "高齢者人口割合",
star.cutoffs = c(0.05, 0.01, 0.001),
add.lines = list(c("AIC", round(AIC(result_ols), 3))),
digits = 3)
##
## 単回帰分析結果
## =================================================
## Dependent variable:
## -----------------------------
## 社会保障費の対GDP比
## -------------------------------------------------
## 高齢者人口割合 141.266***
## (17.675)
##
## Constant 1.624
## (2.592)
##
## -------------------------------------------------
## AIC 523.181
## Observations 93
## R2 0.412
## Adjusted R2 0.406
## Residual Std. Error 3.945 (df = 91)
## F Statistic 63.879*** (df = 1; 91)
## =================================================
## Note: *p<0.05; **p<0.01; ***p<0.001
WORK
- 自身で行ったOLS推定結果を、\(\texttt{stargazer}\)を用いて出力してみよう。
II. OLS推定量の精査:ガウス・マルコフ定理(Gauss-Markov Theorem/Assumptions)
1. 推定量(estimators)の評価基準:推定量はどのような場合に、真の値(true value)から見て良い推定量であるといえるのか?
- 不偏性(unbiasedness):推定量の期待値(\(E(\hat{\theta})\))が、母集団における真の値(\(\theta\))から平均的に見て、過大にも過少にも算出されていないこと
\(E(\hat{\theta})=\theta\) (←\(E(\hat{\theta})-\theta\)は偏り)
⇒不偏性を持つ推定量のことを不偏推定量(unbiased estimator)と呼ぶ
x <- seq(-3, 3, 0.05)
plot(x,dnorm(x, mean=0, sd=0.4), type="l",xlab="Estimated Variance")
curve(dnorm(x, mean=1, sd=0.4), type="l",lty=2,add=T)
text(-1.1,0.6,"unbiased estimator")
text(2.0,0.6,"biased estimator")
abline(v=0, col="blue")
- 有効性(efficiency):推定量の分散が、母集団における真の値から見て、最も小さいこと。他の場合の推定量と比べて、最も分散が小さい(=精度が高い)こと
真の値(\(\theta\))に対して、\(\hat{\theta}_1\)と\(\hat{\theta}_2\)の2つの推定量があり、\(V(\hat{\theta}_1)\leq V(\hat{\theta}_2)\)の時、\(\hat{\theta}_1\)は有効推定量であると相対的に判断
⇒有効性を持つ推定量のことを有効推定量(efiicient estimator)と呼ぶ
x <- seq(-3, 3, 0.05)
plot(x,dnorm(x, mean=0, sd=0.4), type="l",xlab="Estimated Variance")
curve(dnorm(x, mean=0, sd=1.5), type="l",lty=2,add=T)
text(-1.1,0.6,"efficient estimator")
text(2.0,0.2,"inefficient estimator")
abline(v=0, col="blue")
- 一致性(consistency):標本サイズnが大きくなり、母集団Nに近づくと、大数の法則から推定量は真の値に近づく
ではどのような時に、ここまで見てきたOLS推定量は不偏で、有効な線形推定量になるのか?
2. ガウス・マルコフ定理
①線形性:推定量\(\beta_1\)は\(Y_1,...,Y_n\)に対する線形関数である
\(\hat{\beta}=\sum^n_{i=1}a_iY_i.\)
※仮定が侵害される場合:従属変数の形状が離散型確率変数であるために。独立変数と従属変数の間にU字・逆U字などの関係がある場合
②誤差項の期待値:誤差項の期待値はゼロである。全ての\(X\)に対して、誤差項の平均は0になる。
\(E[(e_i|X_{i})]=0\) (→\(E[Y_i|X_{i}]=\beta_{0}+\beta_{1}X_{i}.\))
③誤差間の相関:誤差どうしの相関はゼロである
\(Cov(e_i,e_j)=0,\) \(\forall i\neq j.\)
※仮定が侵害される場合:時系列データや空間データで、系列相関(serial correlation)がある場合。今期(\(t\))の誤差が、前期(\(t-1\))の誤差に影響を受けている場合。
⇒推定量の有効性の侵害
④誤差の分散:誤差の分散は均一(homoskedasticity)である
\(Var(e_i|X_{i})=\sigma^2,\) \(\forall i.\)
※仮定が侵害される場合:従属変数の形状が離散型確率変数であるために、誤差の分散が不均一(heteroskedasticity)である場合。
⇒推定量の有効性の侵害
⑤誤差項と独立変数の相関:誤差項と独立変数の相関はゼロである(=誤差項は独立変数に対して外生的・exoneousである)
\(Cov(e_i,X_{i})=0\)
※仮定が侵害される場合:モデルの特定化(specification)で変数の除去があるために、誤差項と独立変数が相関を持つ場合(内生性・endogeneity)。
⇒推定量の不偏性の侵害
⑥誤差項の正規性:誤差は正規分布に従う(分布解析(\(t\)定・\(F\)検定)を正しく実行するための追加仮定)
\(e_i \sim i.i.d.,\quad N(0,\sigma^2)\)
※仮定が侵害される場合:\(t\)検定や\(F\)検定における誤謬が生じる可能性
OLS推定量がBLUEであるために
①から⑤の仮定が満たされるとき、OLS推定量は…
Best Linear Unbiased Estimator (BLUE): 最良線形不偏推定量
しかし、①から⑤が満たされる、OLS推定量がBLUEとなる場合はかなり限られているのではないかと推察される。どのようにすれば、OLS推定量がBLUEであるかを確かめられるだろうか。そのために、どのような検定が必要だろうか。
3. BLUEをジャッジするための各種検定: 推定量の良し悪しを知るために何をすべきか?
「①線形性」をどのように検定するか?
線形の関係を仮定して行った推定だったが、ひょっとしたら両者の関係は線形のモデルに当てはめるよりも、非線形(non-linear)のモデルに当てはめる方がよいのかもしれない。両者の関係が、U字や逆U字となる場合もあるだろう。それを検討するために、Ramsey-RESET検定を行う。2乗項、3乗項を加えたときに、それらの変数が統計的に有意である可能性はどうか検定する。
#------ Ramsey-RESET検定のためのパッケージ
library(lmtest)
#------ 単回帰モデル result_ols を対象に Ramsey RESET test を実施。2乗項、3乗項を追加したときに追加した項が有意かを確認。追加した項が有意ならば、線形でない可能性
reset_result <- resettest(result_ols, power = 2:3)
print(reset_result)
##
## RESET test
##
## data: result_ols
## RESET = 4.0635, df1 = 2, df2 = 89, p-value = 0.02048
検定結果から、帰無仮説は棄却される。すなわち、2乗項や3乗項を加えた場合に、それらの項が統計的に有意であるの可能性を否定できないという結果。実際にそうなのか確かめてみよう。
以下で2乗項I(peraged^2)
を含んだモデルを推定する。^
がべき乗の処理を意味する。
##
## Call:
## lm(formula = socx_pub ~ peraged + I(peraged^2), data = na.omit(df_ex))
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.2288 -3.0375 -0.1404 2.6462 9.4724
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -34.04 12.69 -2.682 0.008698 **
## peraged 643.22 175.95 3.656 0.000431 ***
## I(peraged^2) -1721.59 600.63 -2.866 0.005170 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.797 on 90 degrees of freedom
## Multiple R-squared: 0.4616, Adjusted R-squared: 0.4496
## F-statistic: 38.58 on 2 and 90 DF, p-value: 7.942e-13
2乗項が統計的に有意に0ではないとは、どういうことを意味しているのだろうか。図示によって確かめてみよう。
df_clean <- df_ex %>%
na.omit()
#------ 散布図と二次回帰曲線(U字型の関係)のプロット
g8 <- df_clean %>%
ggplot(aes(x = peraged, y = socx_pub)) +
geom_point(color = "blue", alpha = 0.5) +
geom_smooth(method = "lm", formula = y ~ x + I(x^2), color = "red", se = TRUE) + #2次式に当てはめることを指示
labs(
x = "65%人口割合",
y = "社会保障費の対GDP比",
title = "両者のlinkは線形?非線形?"
) +
theme_bw()
plot(g8)
この図示から明らかなように、高齢者人口割合が20%に近づく(5人に1人!)になってくると、社会保障費の対GDP比割合は緩やかになり、やや漸減に転じる可能性。1ケタ台から10%台にかけては増えていくのに対して、徐々にカーブしてくことを考えると、線形よりも非線形のモデルの方が望ましい可能性がある。線形性のチェックがいかに重要かわかってもらえるだろう。
「②誤差項の期待値はゼロ」をどのように検定するか?
#------- モデルの残差を抽出
resid_ols <- resid(result_ols)
#------- 残差の平均が0であるかを1標本t検定を用いて検定
t_test_res <- t.test(resid_ols, mu = 0)
print(t_test_res)
##
## One Sample t-test
##
## data: resid_ols
## t = -1.4896e-15, df = 92, p-value = 1
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## -0.8080578 0.8080578
## sample estimates:
## mean of x
## -6.060766e-16
「帰無仮説:true meanが0である」=誤差項の期待値(平均)は0であるを棄却できない。よって、この推定結果からは、誤差項の期待値は0であるとの仮定が満たされることが示唆される
「③誤差項間の相関はゼロ」をどのように検定するか?
誤差項間の相関、系列相関の有無を確かめるために、時系列データに対するDurbin-Watson testとより一般的なBreusch-Godfrey testを行って、系列相関の有無を確かめる。
DW検定の帰無仮説:自己相関は0である
BG検定の帰無仮説:(指定したラグまでの)自己相関は0である
#------ パッケージの読み込み
library(lmtest)
#------ Durbin-Watson検定
dwtest_result <- dwtest(result_ols)
print(dwtest_result)
##
## Durbin-Watson test
##
## data: result_ols
## DW = 0.55457, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: result_ols
## LM test = 47.556, df = 1, p-value = 5.345e-12
DW検定とBG検定で異なる結果が得られている。DW検定の結果にもとづくと、帰無仮説は棄却されず、系列相関を否定できる。それに対して、BG検定では、帰無仮説を棄却できず、系列相関を否定できない。推定量は有効であるとはいえず、誤差の再考が必要と考えられる。
「④誤差項の均一分散」をどのように検定するか?
誤差項の不均一分散(heteroskedasiticity)が生じていないかを確認するために、Breusch-Pagan Cook-Weisberg検定を行う。
- BPCW検定の帰無仮説: 誤差項の分散は均一である。
##
## studentized Breusch-Pagan test
##
## data: result_ols
## BP = 0.38949, df = 1, p-value = 0.5326
推定の結果、誤差項の不均一分散は生じていないことが明らかである。もし誤差項の不均一分散が認められるならば、誤差項の不均一分散に対処し、誤差の過小評価を避けるためにホワイト修正済み頑健誤差(White-adjusted robust error)を計算し報告する。
library(sandwich)
robust_coefs <- coeftest(result_ols, vcov = vcovHC(result_ols, type = "HC1"))
print(robust_coefs)
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.6245 2.6276 0.6182 0.538
## peraged 141.2655 18.1232 7.7947 1.017e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
「⑤誤差項と独立変数間の無相関」をどのように検定するか?
誤差項のと独立変数間の無相関を検定することは、モデルに除外変数(omitted varibles)が存在しないかを検定することである。除外変数無視バイアスにより、推定量は不偏性を持たないことから、除外変数バイアスのために、再びRamsey-RESET検定が必要に。
- Ramsey-RESET検定の帰無仮説: モデルに除外変数はない。
#------ Ramsey-RESET検定のためのパッケージ
library(lmtest)
reset_result_simple <- resettest(result_ols)
print(reset_result_simple)
##
## RESET test
##
## data: result_ols
## RESET = 4.0635, df1 = 2, df2 = 89, p-value = 0.02048
Ramsey-RESET検定の結果からは、モデルに除外変数がある可能性を棄却できない。現段階で、これは当然だろう。1つの要因で、社会保障費の変動を説明できないのは自明である。従って、複数の変数を含めた重回帰分析(multiple regression analysis)を行っていくことになる。
「⑥誤差項の正規性」をどのように検定するか?
まずはqqプロットによって、視覚的に確認を試みる。
直線状に各プロットがより多く一致しているほど、正規性が高いことを意味する。ここでの判断から、かなりの程度誤差の正規性が保たれていることが見て取れるが、やや外れる観察もあるようで判別が難しい。
続いて、正規性検定を行う。まずシャピロ・ウィルク検定(Shapiro-Wilk test)から。
##
## Shapiro-Wilk normality test
##
## data: resid_ols
## W = 0.96412, p-value = 0.01172
帰無仮説「正規分布に従う」が5%水準で棄却されるので、誤差の正規性は否定される。
続いて、Jarque Bera testの結果からは、帰無仮説が棄却されないことが示されている。これらの結果を総合すると、極めて境界的なケースであることが示唆される。
##
## Jarque Bera Test
##
## data: resid_ols
## X-squared = 4.0549, df = 2, p-value = 0.1317
よって総合的に、この推定結果をジャッジしてみよう。回帰診断(regression
diagnostics)を、gvlma()
関数を使って行う。
##
## Call:
## lm(formula = socx_pub ~ peraged, data = na.omit(df_ex))
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.9707 -3.5175 -0.3949 3.0415 9.0872
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.624 2.592 0.627 0.532
## peraged 141.266 17.675 7.992 3.97e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.945 on 91 degrees of freedom
## Multiple R-squared: 0.4124, Adjusted R-squared: 0.406
## F-statistic: 63.88 on 1 and 91 DF, p-value: 3.97e-12
##
##
## ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
## USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
## Level of Significance = 0.05
##
## Call:
## gvlma(x = result_ols)
##
## Value p-value Decision
## Global Stat 12.6870 0.012911 Assumptions NOT satisfied!
## Skewness 1.7258 0.188950 Assumptions acceptable.
## Kurtosis 2.3291 0.126974 Assumptions acceptable.
## Link Function 7.7794 0.005285 Assumptions NOT satisfied!
## Heteroscedasticity 0.8527 0.355781 Assumptions acceptable.
テスト | 検定内容 | 統計量 | p値 | 判定 |
---|---|---|---|---|
全体の結果(Global test) | 以下の4つの検定を総合的に評価する結果 | 12.6870 | 0.012911 | 棄却(全体として仮定を満たさない) |
歪度検定(Skewness check) | 残差が対称分布であるか | 1.7258 | 0.188950 | 棄却せず(歪度に問題なし) |
尖度検定(Kurtosis check) | 残差の裾の形状が正規分布と一致するか | 2.3291 | 0.126974 | 棄却せず(尖度に問題なし) |
リンク関数検定(Link function) | \(Y\)の平均が予測変数に線形に依存するか | 7.7794 | 0.005285 | 棄却(線形性に問題あり) |
均一分散性検定(Heteroskedasticity) | 残差の分散が一定であるか | 0.8527 | 0.355781 | 棄却せず(等分散性に問題なし) |
これらの分析結果からすると、特に\(\texttt{Gloval-test}\)の結果から、本推定結果はガウス・マルコフ仮定を全体として満たさない可能性が高いと判断される。とりわけ問題となるのは、「線形性」のでようである。既述の通り、問題になるのは逆U字性である。
WORK
- 自身が設定したモデルについても、上記の検定を順次行っていこう。その結果を、表に組み込むように工夫してみてほしい。
III. 重回帰分析(multiple regression analysis)
1. 重回帰分析:複数の変数を含む推定
リサーチ・デザインのことを思い出そう。メインの仮説を構築する際に、従属変数に対して影響を与えるほかの要因(制御変数)を考慮した。ここまで行ってきた単回帰では、包括的なモデルを分析できていない。社会保障支出について、どのようなモデルを考案するとよいだろうか。
ここで、classicな研究の追試(replication)を目的として、重回帰分析を実践してみよう。扱う研究は、以下の社会保障政策(福祉国家研究)に関する論文である。この論文では、追試のためのコード(stata)も公開され、当時としては画期的な論文だった。この分析結果を、今再現できるだろうか。
論文の中には、明確にモデルは設定されるのが分かる。そこに「特定化(specification)」という表現が使われていることに注意しよう。これから「モデルの特定化は?specificationはどうなっている?」といったことを聞いていくし、研究を検討する際にも留意するようにしよう。specification次第で、推定結果は異なることもあるし、何より除外変数バイアスの観点からも重要だとわかるだろう。
そのモデルをもとにした推定結果が以下の通りである。
以下で、Allan & Scruggs (2004)の特定化に従って、追試を行う。ここで、なるべく下のコードは見ずに、自分自身でcodebookを探しながら、分析に必要と思われる変数を探してみてほしい。
# ------データの読み込みと変数生成
df_cwd <- read.csv("cws.csv", stringsAsFactors = FALSE) %>%
mutate(
# AS(2004)内での従属変数: 失業手当の置き換え率
urr = rr1y_23,
# メインの独立変数:政権・議会の党派性
# 右派大臣比率(rtcab, rtcrcab, rtctcabの和)
rt = rtcab + rtcrcab + rtctcab,
# 右派議席比率(既存のrtseatにrtcrseat, rtctseatを加算)
rtseat = rtseat + rtcrseat + rtctseat,
# 左派大臣比率(leftcabをそのまま使用)
lt = leftcab,
# 制御変数(共変量)
# 貿易自由度:tradeopenの対数変換(※hist()による分布確認は省略)
tr_open = log(tradeopen),
# 金融自由度:kaopen
fi_open = kaopen,
# 拒否点:fed
veto = fed,
# GDP:rgdpecap
gdp = log(rgdpecap),
# 負債:govdefの対数変換(※hist()による分布確認は省略)
deficit = log(govdef),
# コーポラティズム:ep
corp = ep,
# 疾病手当:sickgen
sick = sickgen
)
2. 推定結果の示し方
AS (2004)でもそうであるように、推定結果は複数の特定化から構成されている。いくつかの推定結果を試してみよう。
library(stargazer)
# ------lm()による回帰分析の実行
# (1) 左派大臣モデル
mlt <- lm(urr ~ lt + tr_open + fi_open + veto + gdp + deficit + corp + sick,
data = df_cwd)
summary(mlt)
##
## Call:
## lm(formula = urr ~ lt + tr_open + fi_open + veto + gdp + deficit +
## corp + sick, data = df_cwd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.37932 -0.08971 -0.00211 0.09841 0.49354
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -7.5575707 2.3121692 -3.269 0.002712 **
## lt -0.0003530 0.0008698 -0.406 0.687720
## tr_open 0.2209466 0.1507485 1.466 0.153144
## fi_open -0.0481786 0.0474611 -1.015 0.318167
## veto 0.0750193 0.0689491 1.088 0.285242
## gdp 0.6945968 0.2418915 2.872 0.007426 **
## deficit 0.0032433 0.0251350 0.129 0.898190
## corp -0.0103321 0.0999237 -0.103 0.918334
## sick 0.0351040 0.0089544 3.920 0.000475 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2005 on 30 degrees of freedom
## (1171 個の観測値が欠損のため削除されました)
## Multiple R-squared: 0.4835, Adjusted R-squared: 0.3458
## F-statistic: 3.511 on 8 and 30 DF, p-value: 0.005616
# (2) 右派大臣モデル
mrt <- lm(urr ~ rt + tr_open + fi_open + veto + gdp + deficit + corp + sick,
data = df_cwd)
summary(mrt)
##
## Call:
## lm(formula = urr ~ rt + tr_open + fi_open + veto + gdp + deficit +
## corp + sick, data = df_cwd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.29047 -0.11197 -0.02480 0.07556 0.43252
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -9.440333 2.447506 -3.857 0.000565 ***
## rt -0.001726 0.001137 -1.518 0.139442
## tr_open 0.228516 0.145694 1.568 0.127261
## fi_open -0.027940 0.045630 -0.612 0.544933
## veto 0.070127 0.066432 1.056 0.299567
## gdp 0.883989 0.255729 3.457 0.001656 **
## deficit 0.006233 0.024374 0.256 0.799904
## corp -0.035264 0.097877 -0.360 0.721151
## sick 0.034920 0.008546 4.086 0.000301 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1938 on 30 degrees of freedom
## (1171 個の観測値が欠損のため削除されました)
## Multiple R-squared: 0.5177, Adjusted R-squared: 0.3891
## F-statistic: 4.026 on 8 and 30 DF, p-value: 0.002402
# (3) 左派議席モデル:単回帰
# (ここではleftseatはすでにデータ中に存在している前提です)
mlts <- lm(urr ~ leftseat,
data = df_cwd)
summary(mlts)
##
## Call:
## lm(formula = urr ~ leftseat, data = df_cwd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.45518 -0.14408 -0.04483 0.16102 0.43814
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.4087942 0.0238597 17.133 <2e-16 ***
## leftseat 0.0012955 0.0005897 2.197 0.0285 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2087 on 486 degrees of freedom
## (722 個の観測値が欠損のため削除されました)
## Multiple R-squared: 0.009832, Adjusted R-squared: 0.007795
## F-statistic: 4.826 on 1 and 486 DF, p-value: 0.02851
##
## Call:
## lm(formula = urr ~ rtseat, data = df_cwd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.49714 -0.13618 -0.03508 0.15449 0.44926
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.5248728 0.0241583 21.726 < 2e-16 ***
## rtseat -0.0017777 0.0005822 -3.053 0.00239 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2078 on 486 degrees of freedom
## (722 個の観測値が欠損のため削除されました)
## Multiple R-squared: 0.01882, Adjusted R-squared: 0.0168
## F-statistic: 9.324 on 1 and 486 DF, p-value: 0.002386
# (5) 左派議席率モデル
mltss <- lm(urr ~ leftseat + tr_open + fi_open + veto + gdp + deficit + corp + sick,
data = df_cwd)
summary(mltss)
##
## Call:
## lm(formula = urr ~ leftseat + tr_open + fi_open + veto + gdp +
## deficit + corp + sick, data = df_cwd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.33092 -0.09947 0.00204 0.10879 0.41378
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -6.8020945 1.9944499 -3.411 0.00187 **
## leftseat -0.0164973 0.0053166 -3.103 0.00415 **
## tr_open 0.2472837 0.1317384 1.877 0.07026 .
## fi_open -0.0926740 0.0431684 -2.147 0.04001 *
## veto 0.0651785 0.0600092 1.086 0.28607
## gdp 0.6663783 0.2078403 3.206 0.00319 **
## deficit 0.0007276 0.0219375 0.033 0.97376
## corp 0.0606123 0.0901645 0.672 0.50658
## sick 0.0466792 0.0086503 5.396 7.61e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1749 on 30 degrees of freedom
## (1171 個の観測値が欠損のため削除されました)
## Multiple R-squared: 0.6069, Adjusted R-squared: 0.502
## F-statistic: 5.789 on 8 and 30 DF, p-value: 0.0001708
# (6) 右派議席率モデル
mrtss <- lm(urr ~ rtseat + tr_open + fi_open + veto + gdp + deficit + corp + sick,
data = df_cwd)
summary(mrtss)
##
## Call:
## lm(formula = urr ~ rtseat + tr_open + fi_open + veto + gdp +
## deficit + corp + sick, data = df_cwd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.39612 -0.10821 0.00525 0.08472 0.36959
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -9.0928500 2.2087115 -4.117 0.000277 ***
## rtseat -0.0072241 0.0034454 -2.097 0.044548 *
## tr_open 0.1730598 0.1430762 1.210 0.235892
## fi_open -0.0302191 0.0435563 -0.694 0.493148
## veto 0.1450850 0.0730186 1.987 0.056125 .
## gdp 0.9013415 0.2399495 3.756 0.000742 ***
## deficit -0.0006294 0.0235982 -0.027 0.978898
## corp -0.0656456 0.0971455 -0.676 0.504379
## sick 0.0313888 0.0084126 3.731 0.000795 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1878 on 30 degrees of freedom
## (1171 個の観測値が欠損のため削除されました)
## Multiple R-squared: 0.5471, Adjusted R-squared: 0.4263
## F-statistic: 4.529 on 8 and 30 DF, p-value: 0.001084
WORK
重回帰分析のモデルを独自に構築しよう。AS (2004)をベースとしても良いし、独自のモデル構築を行っても良い。
構築したモデルをもとに、周囲の人に説明し、議論をしてみよう。
上記の結果が羅列されても、どの特定化を採用していいかなど判別がつかない。よって、推定結果を一覧表にして示す。
# ------stargazerによる結果の出力
# コンソールへのテキスト出力
stargazer(mlt, mrt, mlts, mrts, mltss, mrtss,
type = "text",
title = "失業手当の置換率に関するOLS推定結果",
dep.var.labels = "urr",
out.header = FALSE)
Word形式で出力することも可能。うまく出力ができているなら、作業ディレクトリ内に「\(\texttt{OLS_results.doc}\)」というWORDファイルが生成されているはず。作業ディレクトリにデータが保存されるか、確かめてみよう。出力されたワードファイル内の独立変数名を、日本語のペーパーならば日本語に、英語のペーパーであっても英単語や熟語に修正して報告してほしい。
# Word形式での出力:作業ディレクトリに保存されている
stargazer(mlt, mrt, mlts, mrts, mltss, mrtss,
type = "html",
title = "失業手当の置換率に関するOLS推定結果",
dep.var.labels = "urr",
out = "OLS_results.doc")
次に、推定結果を図にして示すことを考えよう。係数図(coefficient plot)は次のように描画できる。
library(coefplot)
#------ coefplotによる係数プロットの作成
coefplot(mlt,
main = "Coefficient Plot - Left Minister Model",
xlab = "Coefficient Estimate",
ylab = "Covariates",
intercept = FALSE,
interceptName = "(Intercept)" # 定数項は抜いて描画という指示
)
これでも十分に見やすいプロットだが、他の図示の方法もある。好みにはなるが…
library(broom)
library(dotwhisker)
#------ 得られた分析結果に関するデータの成形
df_coef <- tidy(mlt, conf.int = TRUE) %>%
filter(term != "(Intercept)")
#------ 変数名の変換
df_coef <- df_coef %>%
relabel_predictors(
lt = "左派大臣比率",
tr_open = "貿易自由度",
fi_open = "金融自由度",
veto = "拒否点",
gdp = "GDP成長率",
deficit = "負債",
corp = "コーポラティズム",
sick = "疾病手当"
)
#------ 図示
dwplot(df_coef, show_intercept = FALSE) +
theme_bw() +
labs(
title = "左派大臣モデルの係数プロット",
x = "係数",
y = ""
)
library(jtools)
#------ 得られた分析結果に関するデータの成形と図の出力を一括して行う
plot_summs(mlt,
coefs = c(
"左派大臣比率" = "lt",
"貿易自由度" = "tr_open",
"金融自由度" = "fi_open",
"拒否点" = "veto",
"GDP成長率" = "gdp",
"負債" = "deficit",
"コーポラティズム" = "corp",
"疾病手当" = "sick"
),
title = "左派大臣モデルの係数プロット",
x.label = "係数"
) +
theme_bw()
意外と応用が利くのが、以下のDIYで作る手法。様々な分析結果に対応できる。柔軟な作図が可能になる。
library(forcats)
#------ 得られた分析結果に関するデータの成形
df_coef <- tidy(mlt, conf.int = TRUE) %>%
filter(term != "(Intercept)")
#------ 変数名の変換
df_coef <- df_coef %>%
mutate(
term = dplyr::recode(term,
lt = "左派大臣比率",
tr_open = "貿易自由度",
fi_open = "金融自由度",
veto = "拒否点",
gdp = "GDP成長率",
deficit = "負債",
corp = "コーポラティズム",
sick = "疾病手当"
),
# reverse so the first recoded term sits at the top
term = fct_rev(fct_inorder(term))
)
#------ 図示
ggplot(df_coef, aes(x = estimate, y = term)) +
geom_point() +
geom_errorbarh(aes(xmin = conf.low, xmax = conf.high), height = 0.2) +
theme_bw() +
labs(
title = "左派大臣モデルの係数プロット",
x = "係数",
y = NULL
)
複数のモデルの結果の描画ができれば、結果を一覧性をもって見比べることができる。
#------ 得られた分析結果に関するデータの成形
df_left <- tidy(mlt, conf.int = TRUE) %>%
filter(term != "(Intercept)") %>%
mutate(model = "左派大臣比率")
df_right <- tidy(mrt, conf.int = TRUE) %>%
filter(term != "(Intercept)") %>%
mutate(model = "右派大臣比率")
df_all <- bind_rows(df_left, df_right)
#------ 変数名の変換
df_all <- df_all %>%
mutate(
term = dplyr::recode(term,
lt = "左派大臣比率",
rt = "右派大臣比率",
tr_open = "貿易自由度",
fi_open = "金融自由度",
veto = "拒否点",
gdp = "GDP成長率",
deficit = "負債",
corp = "コーポラティズム",
sick = "疾病手当"
),
# 項目を元の並びで表示したいなら fct_inorder()
term = fct_rev(fct_inorder(term))
)
#------ 点や線をずらすdodge の幅を設定
pd <- position_dodge(width = 0.7)
#------ 図示
ggplot(df_all, aes(x = estimate, y = term, color = model)) +
geom_point(position = pd, size = 3) +
geom_errorbarh(aes(xmin = conf.low, xmax = conf.high),
position = pd, height = 0.2) +
theme_bw() +
labs(
title = "左派・右派大臣モデルの係数プロット",
x = "係数",
y = NULL,
color = "Model"
)
これらの図は便利だし、一覧性にも優れている。しかし、上記で行ったような各種検定結果(Ramsey-RESET検定、Breusch-Goldfrey検定、Breusch-Pagan Cook-Weisberg検定等)を掲示できない。ペーパーで係数図を作成したときに、必ず回帰診断の結果を併記するようにしよう。
WORK
- 推定結果を図表にしてみてほしい。それをもとに、改めて周囲の人たちも議論をしてみてほしい。
3. 交差項を含んだ分析
第2回のレジュメでも説明したように、条件付け仮説を分析する場合もある。ここでは、
条件付け仮説:議院内閣制の下で、左派議席率が増えるほど、社会保障費が増える
を検証しよう。この仮説を検証するためには、大統領制(準大統領制も含む)=1と議院内閣制=0の場合で、左派議席率が社会保障支出に与える影響を検証するとよい。ここまで使ってきた、失業率の置換率はいったんやめて、初めに利用していた\(\texttt{socx_pub}\)変数を利用しよう。
#------ 交差項を含むモデルの推定
df_ex <- df_ex %>%
select(socx_pub, leftseat, pres) %>%
na.omit() %>%
mutate(
執政制度 = case_when(
pres == 0 ~ "議院内閣制",
pres == 1 ~ "大統領制"
),
執政制度 = factor(執政制度, levels = c("議院内閣制","大統領制"))
)
model <- df_ex %>%
dplyr::select(socx_pub, leftseat, pres) %>%
na.omit() %>%
with(lm(socx_pub ~ leftseat * pres))
summary(model)
##
## Call:
## lm(formula = socx_pub ~ leftseat * pres)
##
## Residuals:
## Min 1Q Median 3Q Max
## -12.3461 -3.0448 -0.2946 3.7051 14.4235
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 17.91504 0.64001 27.992 < 2e-16 ***
## leftseat 0.07581 0.01506 5.033 6.28e-07 ***
## pres -2.70044 0.98499 -2.742 0.00628 **
## leftseat:pres 0.13593 0.02675 5.081 4.92e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.745 on 645 degrees of freedom
## Multiple R-squared: 0.1537, Adjusted R-squared: 0.1497
## F-statistic: 39.04 on 3 and 645 DF, p-value: < 2.2e-16
この推定結果を基にすると、\(\texttt{pres}\)の値に応じた数式は次のように書ける;
- 議院内閣制(
pres=0
)の場合:
\[ \begin{align*} \hat{socx\_pub}=17.91504+0.07581\times \mathrm{leftseat} \end{align*} \]
- 大統領制(
pres=1
)の場合:
\[ \begin{align*} \hat{socx\_pub} &= (17.9150-2.70044) + (0.07581 + 0.13593)\times \mathrm{leftseat} \\ &= 15.21460 + 0.21174 \times \mathrm{leftseat} \end{align*} \]
交差項を含んだモデルにおいて、それぞれの係数や定数項はどのような意味を持つか、どのように解釈できるかは以下表の通りである。
係数名 | 推定値 | 意味 |
---|---|---|
(Intercept) | 17.9150 | leftseat = 0, pres = 0 のときの予測値(議院内閣制・左派議席率 0 のとき) |
leftseat | 0.0760 | pres = 0 のとき、左派議席率が 1 ポイント上がると socx_pub が 0.456 増加 |
pres | -2.7044 | leftseat = 0 のとき、議院内閣制→大統領制 に切り替わると切片が +2.789 変化 |
leftseat:pres | 0.1359 | 大統領制 (pres=1) では、左派議席率の傾きがさらに −0.123 変化→大統領制になることで左派議席率の効果が-0.123減殺される |
この結果を図示してみよう。pres変数の値によって、異なる回帰直線と散布図を描画するように求めている。
#------- pres変数の値によって、異なる回帰直線を描画するように指示
df_ex %>%
select(socx_pub, leftseat, pres) %>%
na.omit() %>%
ggplot(aes(x = leftseat, y = socx_pub,
color = factor(pres),
fill = factor(pres))) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm",
formula = y ~ x,
se = TRUE,
alpha = 0.2) +
scale_color_manual(
name = "執政制度",
values = c("0" = "steelblue", "1" = "tomato"),
labels = c("0" = "議院内閣制", "1" = "大統領制")
) +
scale_fill_manual(
name = "執政制度",
values = c("0" = "steelblue", "1" = "tomato"),
labels = c("0" = "議院内閣制", "1" = "大統領制")
) +
labs(
x = "左派議席率",
y = "社会保障費の対GDP比",
title = "執政制度と左派議席率間の交差項に関するプロット"
) +
theme_bw()
他に、packageを使って描画する方法もある。まずは\(\texttt{visreg}\) packageを使う場合。
library(visreg)
model <- df_ex %>%
select(socx_pub, leftseat, 執政制度) %>%
na.omit() %>%
with(lm(socx_pub ~ leftseat * 執政制度))
visreg(model,
xvar = "leftseat",
by = "執政制度",
overlay = TRUE,
gg = TRUE,
xlab = "左派議席率",
ylab = "社会保障費の対GDP比",
title = "執政制度と左派議席率間の交差項に関するプロット",
legend.title= "執政制度"
) +
scale_color_manual(name = "執政制度",
values = c("議院内閣制"="steelblue", "大統領制"="tomato")) +
theme_bw()
次に、\(\texttt{ggeffects}\) packageを使う場合。
library(ggeffects)
ggdf <- ggpredict(model, terms = c("leftseat [all]", "執政制度"))
ggplot(ggdf, aes(x = x, y = predicted,
color = group, fill = group)) +
geom_ribbon(aes(ymin = conf.low, ymax = conf.high),
alpha = 0.2, color = NA) +
geom_line(size = 1) +
scale_color_manual(name = "執政制度",
values = c("議院内閣制" = "steelblue",
"大統領制" = "tomato")) +
scale_fill_manual(name = "執政制度",
values = c("議院内閣制" = "steelblue",
"大統領制" = "tomato")) +
labs(x = "左派議席率",
y = "社会保障費の対GDP比",
title = "執政制度と左派議席率間の交差項に関するプロット") +
theme_bw()
これらの交差項プロットは、散布図があったりなかったりと様々だがどのようなことが読み取れるだろうか。まず直線に付随する誤差の帯(error band)は95%信頼区間を表す。95%信頼区間がy=0線をまたぐならば、その区間において当該の係数は0であることを否定できないと解釈する。
いまいずれの直線においても、95%信頼区間がy=0の領域に位置するとは判断できる区間がないことから、左派議席率が0から60%という区間全てにおいて(現実世界における左派議席率の実現値の区間において)、左派議席率は社会保障費の増額に寄与していると判断することになる。但し、それが議院内閣制と大統領制の場合で、異なる傾きを持っていることがわかる。
次に2本の直線が交わっているので、交点を計算してみよう。交点は以下の式から求められる。
\[ \alpha_0+\beta_0x=\alpha_1+\beta_1x \Rightarrow x=\frac{\alpha_1-\alpha_0}{\beta_0-\beta_1} \]
上の式を考慮したうえで、以下で交点に関わる情報を図内に反映させる。
#------ データを準備
df_plot <- df_ex %>%
select(socx_pub, leftseat, pres) %>%
na.omit()
#------ pres = 0 / 1 それぞれのモデルを計算・フィットさせる
mod0 <- lm(socx_pub ~ leftseat, data = df_plot, subset = pres == 0)
mod1 <- lm(socx_pub ~ leftseat, data = df_plot, subset = pres == 1)
#------ 切片と傾きを取り出し、交点を計算
a0 <- coef(mod0)["(Intercept)"]; b0 <- coef(mod0)["leftseat"] #上記の計算式を反映したもの
a1 <- coef(mod1)["(Intercept)"]; b1 <- coef(mod1)["leftseat"]
x_int <- (a1 - a0) / (b0 - b1)
y_int <- a0 + b0 * x_int
#------ 図示
ggplot(df_plot,
aes(x = leftseat,
y = socx_pub,
color = factor(pres),
fill = factor(pres))) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm",
formula = y ~ x,
se = TRUE,
alpha = 0.2) +
# 交点の垂線 + ラベルの表記を指示
geom_vline(xintercept = x_int, linetype = "dashed") +
annotate("text",
x = x_int,
y = max(df_plot$socx_pub, na.rm = TRUE),
label = paste0("X = ", round(x_int, 2)),
hjust = -0.1,
vjust = 1) +
scale_color_manual(
name = "執政制度",
values = c("0" = "steelblue", "1" = "tomato"),
labels = c("0" = "議院内閣制", "1" = "大統領制")
) +
scale_fill_manual(
name = "執政制度",
values = c("0" = "steelblue", "1" = "tomato"),
labels = c("0" = "議院内閣制", "1" = "大統領制")
) +
labs(
x = "左派議席率",
y = "社会保障費の対GDP比",
title = "執政制度ごとの回帰直線と交点"
) +
theme_bw()
この図から、\(x=19.87\)、つまり左派政党の議席比率が約20%を越えるあたりを境として、議院内閣制の場合と大統領制の場合で異なる状況が生じることを示唆している。つまり…
左派議席率が約20%より下の時:議院内閣制の方が予測される社会保障支出対GDP比が高い
左派議席率が約20%より上の時:大統領制の方が予測される社会保障支出対GDP比が高い
従って、「条件付け仮説:議院内閣制の下で、左派議席率が増えるほど、社会保障費が増える」は、左派議席比率が約20%以下の時には妥当だが、左派議席比率が約20%を越えるとあてはまらないという評価になる。
WORK
条件付けの仮説を考えていた人は、それを実践してみよう。
まだ考えてはいなかった人も、複数の条件付けを仮説を検討し、実践してみほしい。
調整変数を質的変数にした場合どうだろうか?二値変数の場合、順序変数の場合で検討してみよう。
IV. パネル・データの分析
1. パネル・データの分析とは何か?
- 個体(国/地域/グループ/個人など)に関する複数期間(年/月/日/時間)からなるデータ。
第1回授業のデータの再掲
2. パネル・データはなぜ望ましいのか?
選択バイアスの問題
調査時点の母集団の中でデータの収集が可能、回答が可能なサンプルは常に限られる(例:各国の中にはデータが取れない国もある/高齢者がアンケートに答えてくれない/不真面目な人はきちんと答えてくれないなどたくさん!)
⇒データの収集には観察されたサンプリングに対して、母集団を反映できない選択の偏り(selection bias)が不可避。また、観察できない要素≒データから漏れる要素も多く存在する
パネル・データの特性を考える①:固定効果モデル
パネル・データを使うことの利点を固定効果モデル(Fixed Effect model: FE model)の説明をもとに考えていこう。
通常の最小二乗法(OLS)のモデルは下記の通り;
\[y_{it}=\beta_{0}+\alpha z_{i}+\beta x_{it}+\upsilon_{it}.\]
\(y_{it}\)はサンプル\(i\)の\(t\)期のの生活費、\(z_{i}\)は職種などのいったん決まったら時間的に変化しない変数、\(x_{it}\)は\(t\)期の給料のように時期ごとに変化する変数、\(\upsilon_{it}\)は観察できない誤差であるとする。この誤差の中には、「真の誤差」に加えて、個人\(i\)が個人\(i\)であることによる属性・要因(個性、性質、性向、特性)などが含まれる。その特性を\(\mu_{i}\)、真の誤差を\(\epsilon_{it}\)とすると、上式は以下のように書き直せる;
\[y_{it}=\beta_{0}+\alpha z_{i}+\beta x_{it}+\mu_{i}+\epsilon_{it}.\]
\(\upsilon_{it}\)は\(\mu_{i}+\epsilon_{it}\)として書き直されていることになるが、ここで\(\mu_{i}\)は各国の歴史的文脈のようなものでデータ化が困難であったり、アンケートでも質問が困難であったりする観察不可能な個体固有成分の異質性(heterogeneity)である。ここで説明変数と\(\mu_{i}\)の間に相関があるならば、\(\beta\)の推定値はバイアスをもつ。
ここで、個人間(between)で要因が変動すると考えると、変化する変数の個人の平均をもとに、以下の式を設定できる;
\[\bar{y_{i}}=\alpha z_{i}+\beta\bar{x}_{i}+\mu_{i}+\epsilon_{i}\]
バイアスとなっている\(\mu_{i}\)を除去するために、上2本の式の差をとると;
\[(y_{it}-\bar{y}_{i})=\beta(x_{it}-\bar{x}_{i})+(\epsilon_{it}-\bar{\epsilon}_{i})\]
が導かれ、観測できな要因である\(\mu_{i}\)を除いた推定量を導き、個人内での独立変数の変動によって、従属変数の変動を説明することが可能になる。パネル推定の固定効果推定量が一致推定量ならば、
推定される\(\beta\)は「同じ個体内で\(x\)が変わった時の平均的な影響」となる
時点で不変(time-invariant)な\(z_i\)も消えてしまうため、係数\(\alpha\)は推定できない。
説明変数と\(\mu_i\)の相関が許容されることで内生性バイアスを回避可能
といった利点がある。一方で、時変しない変数(執政制度、選挙制度、民主主義の程度など)の効果は測定できないため注意を要する。
パネル・データの特性を考える②:ランダム効果モデル(Random Effect model: RE model)
固定効果モデルにおいては「\(\mu_i\)」をダミー変数で消したが、ランダム効果モデルは\(\mu_i\)を確率的にランダムな係数とみなす;
\[ y_{it} = \beta_0 + \alpha\,z_i + \beta\,x_{it} + \upsilon_i + \epsilon_{it},\quad \upsilon_i\sim \mathrm{i.i.d.}(0,\sigma_\upsilon^2),\ \epsilon_{it}\sim \mathrm{i.i.d.}(0,\sigma_\epsilon^2).\]
上式においては、個体固有成分\(\upsilon_i\)はすべての説明変数と無相関であり、\(\epsilon_{it}\)は個体・時点ともに独立であることが仮定されることになる。
ランダム効果推定量の特徴として、以下の点が挙げられる。
時点で不変な変数\(z_i\)の係数\(\alpha\)も推定可能
無相関の仮定が満たされるときにFEより分散が小さく有効性が高い
無相関の仮定が侵害されているならば(\(\upsilon_i\)と説明変数が相関)しているならば不偏推定量にならない
\(\mu_i\)または\(\upsilon_i\)に関係して、Wu-Hausman 検定によりFEとREの推定値の差に関する統計的有意性の判別が必要に。
3. パネル・データ分析の実践:固定効果モデルか、ランダム効果モデルか?
パネル・データ分析の設定
ここまでの分析でも、ある程度リサーチ・デザインは意識してきたが、以下では改めてリサーチ・デザインを明示する。
研究の設計:各国の社会保障費は、何によって規定されているのか。
メインの仮説の設定:
- メインの仮説1:左派政党の議席率が高いほど、社会保障費が減る
対立仮説の検討:
対立仮説1:高齢者人口費が高まるほど、社会保障費が増える
対立仮説2:比例代表制の場合ほど、社会保障費が増える
対立仮説3:投票率が高いほど、社会保障費が増える
利用するデータ:Comparative Welfare State dataset
変数の設定:
従属変数:社会保障費の対GDP比
カギとなる独立変数(key independent variables):左派議席比率
制御変数(control variables):高齢者人口比、 選挙制度ダミー、失業率、インフレ率、投票率
パネル・データの推定
パネル・データのプロット。社会保障費の対GDP比に対する仮説に対応した各要因について、国別にパネルを折りたたみながら図示してみよう。
まずはメインの独立変数である左派議席率について検討する。左派議席率が増えることによって、社会保障支出が増えている国はポーランド、スウェーデンぐらいで、左派議席率の増加に伴って、社会保障支出が減る国、あるいは影響が認められないと思われる国が多いようである。
#------ データ読み込みと整形
df_cws <- read.csv("cws.csv", fileEncoding = "SJIS") %>%
transmute(
id,
year,
socx_pub,
popold = po65 / pop,
leftseat,
vturn,
singmemd,
hunemr,
cpi
)
#------ 国別のパネル図
df_cws %>%
ggplot(aes(x = leftseat, y = socx_pub)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "steelblue", fill = "lightblue", alpha = 0.3) +
facet_wrap(~ id, scales = "free") +
theme_bw() +
labs(
title = "国別:左派議席比率と社会保障支出",
x = "左派議席比率",
y = "社会保障費対GDP比"
)
高齢者人口割合については以下の通り。多くの国で、高齢者人口割合の増加が社会保障支出を押し上げていることが分かる。I-2内の各種散布図を国別に仕分けたものだが、I-2内の各散布図が各国の結果を集積したものであるとわかるように、各国で明確な正の結びつきが見てとれる。
#------ 国別のプロット:高齢者人口割合
df_cws %>%
ggplot(aes(x = popold, y = socx_pub)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "steelblue", fill = "lightblue", alpha = 0.3) +
facet_wrap(~ id, scales = "free") +
theme_bw() +
labs(
title = "国別:65歳以上人口比と社会保障支出",
x = "65歳以上人口比",
y = "社会保障費対GDP比"
)
投票率については、左派議席率の結果とよく似て、明確な傾向は確かめられない。
#------ 国別のプロット:投票率
df_cws %>%
ggplot(aes(x = vturn, y = socx_pub)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "steelblue", fill = "lightblue", alpha = 0.3) +
facet_wrap(~ id, scales = "free") +
theme_bw() +
labs(
title = "国別:左派議席比率と社会保障支出",
x = "投票率",
y = "投票率"
)
続いて、既述の固定効果モデルとランダム効果モデルの推定を順に行い、どちらが一致推定量(consistent estimator)となるかを評価する。手順は、
①固定効果モデル推定の実施
②ランダム効果モデル推定の実施
③どちらが一致推定量かについてのハウスマン検定(Wu-Hausman test)の実施
\(H_0\): 固定効果モデルとの差が偶然である(ランダム効果モデルが、一致推定量として望ましい
\(H_1\): 固定効果モデルとの差が偶然ではない(固定効果モデルが、一致推定量として望ましい)
となる。上記の推定から検定までを、効率化されたコードをもとに実行する。まずはなるべく、以下のコードを見ずに、パネル・データの推定モデルはどのようにすれば実施できるか、どのようにすればハウスマン検定ができるか、それらのコードを効率化させるにはどうすればよいか自身で考えて、多くのエラーを出しながら頑張ってみてほしい。
library(plm) #パネル・データ分析用のパッケージ
library(lmtest) #Wu-Hausman testのためのパッケージ
hausman_result <-
read.csv("cws.csv", fileEncoding = "SJIS") %>%
# データ整形
transmute(
id,
year,
socx_pub,
popold = po65 / pop,
leftseat,
vturn,
prsys = case_when(
singmemd == 0 ~ 1,
singmemd %in% c(1,2) ~ 0,
TRUE ~ NA_real_
),
hunemr,
cpi
) %>%
{
# 固定効果モデルの推定
model_FE <- plm(socx_pub ~ popold + leftseat + vturn + prsys + hunemr + cpi,
data = .,
index = c("id", "year"),
model = "within")
# ランダム効果モデル
model_RE <- plm(socx_pub ~ popold + leftseat + vturn + prsys + hunemr + cpi,
data = .,
index = c("id", "year"),
model = "random")
# モデル要約を出力
print(summary(model_FE))
print(summary(model_RE))
# Wu-Hausman検定
phtest(model_FE, model_RE)
}
## Oneway (individual) effect Within Model
##
## Call:
## plm(formula = socx_pub ~ popold + leftseat + vturn + prsys +
## hunemr + cpi, data = ., model = "within", index = c("id",
## "year"))
##
## Unbalanced Panel: n = 21, T = 11-31, N = 537
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -4.171415 -0.937344 -0.032683 0.835710 6.086397
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## popold 52.5319548 6.1969363 8.4771 2.499e-16 ***
## leftseat -0.0107755 0.0099337 -1.0847 0.2785
## vturn 0.0300136 0.0221282 1.3564 0.1756
## prsys -0.4616750 0.4052846 -1.1391 0.2552
## hunemr 0.4261277 0.0325927 13.0743 < 2.2e-16 ***
## cpi 0.0529909 0.0054992 9.6362 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 2646.2
## Residual Sum of Squares: 1232.1
## R-Squared: 0.53437
## Adj. R-Squared: 0.51064
## F-statistic: 97.5497 on 6 and 510 DF, p-value: < 2.22e-16
## Oneway (individual) effect Random Effect Model
## (Swamy-Arora's transformation)
##
## Call:
## plm(formula = socx_pub ~ popold + leftseat + vturn + prsys +
## hunemr + cpi, data = ., model = "random", index = c("id",
## "year"))
##
## Unbalanced Panel: n = 21, T = 11-31, N = 537
##
## Effects:
## var std.dev share
## idiosyncratic 2.416 1.554 0.136
## individual 15.292 3.911 0.864
## theta:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.8810 0.9237 0.9237 0.9221 0.9264 0.9288
##
## Residuals:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -4.4805 -1.0368 -0.1162 -0.0049 0.9080 6.4994
##
## Coefficients:
## Estimate Std. Error z-value Pr(>|z|)
## (Intercept) 3.5801631 2.3291024 1.5371 0.12426
## popold 55.0774946 6.0982487 9.0317 < 2e-16 ***
## leftseat -0.0075766 0.0097797 -0.7747 0.43850
## vturn 0.0396441 0.0213939 1.8531 0.06387 .
## prsys -0.3019353 0.3987817 -0.7571 0.44896
## hunemr 0.4221173 0.0324805 12.9960 < 2e-16 ***
## cpi 0.0526473 0.0054927 9.5849 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 2760.2
## Residual Sum of Squares: 1287.4
## R-Squared: 0.53361
## Adj. R-Squared: 0.52833
## Chisq: 588.888 on 6 DF, p-value: < 2.22e-16
##
## Hausman Test
##
## data: socx_pub ~ popold + leftseat + vturn + prsys + hunemr + cpi
## chisq = 9.1342, df = 6, p-value = 0.1662
## alternative hypothesis: one model is inconsistent
本推定モデルの場合、帰無仮説は棄却されないことがわかる。ランダム効果推定量を採用することになる。
4. 差分の差法(Difference in Difference: DID)
DIDとは何か?
パネル・データを用いて行う、有力な自然実験(natural experiment)の一つに「差分の差法(Difference in Difference: DID)」がある。
下記図のように、何らかの政策介入、構造変化に実際に効果があったのか、そのもとで関心のある変数に効果差が生じていたのかを知りたいケースは、公共政策の評価において多々あるだろう。
政策介入が行われるグループとそうでないグループが分かれ、時間経過のもとに政策介入によって変化が生じたのであれば、それを自然に生じた実験の結果とみなせる。DIDでは、
政策介入が起こった群と起こっていない群の時間的経過
構造変化が起こった群と起こっていない群の時間的経過
これらについて、処置群と対照群との変化の差をとって、さらにその差をとることにより、共通トレンド以外の要因を取り除くことにより、政策介入の因果効果の特定を目指す。
DIDの実践
先ほどから取り上げている、AS (2004: 505)には、構造変化(structural break)について、次のような記述がある。
The fourth point relates to the specification of interaction terms. In order to capture the “sea change” that occurred in the 1980s and suggested in much of the literature on contemporary welfare states, we needed to locate this structural break in a manner that was both nonarbitrary but consistent with the literature. We used the year of the major economic recession during the early 1980s in each country. We then interacted each of the substantive variables with that break. This results in a dummy-variable-interaction model that allows us to evaluate relationships between the substantive variables before and after the critical break year.
さらに、AS (2004: FN20, 505)では、構造変化としてこれらの時期が想定されているという。
Specifically, for each country, the break is coded 1 for years after the last negative (or the lowest) growth year in the first half of the 1980s. Years before that event (inclusive) are coded 0. Individual break points were: 1978: New Zealand (results are not altered if the near recession in 1986 is substituted); 1981: Austria, Belgium, Denmark, Finland, Sweden, and United Kingdom; 1982: Canada, Germany, Italy, Netherlands, Norway, Switzerland, and United States; 1983: Australia, France, Ireland, and Japan.
上記のもとに、以下の研究設計を検討し、DIDとして実践してみたい(政策介入とは異なるので、自然実験のデザインとしては疑問もあるが…)。
研究の設計:1980年代に民主主義体制の各国で生じた不況の構造変化は、各国の社会保障費を押し上げたのか。また、そこで左派政党が果たした役割は、どのようなものだったと考えられるか。
メインの仮説の設定:
- メインの仮説1:1980年代に民主主義体制の各国で生じた不況の構造変化によって、各国の社会保障は拡充された。
利用するデータ:Comparative Welfare State dataset
変数の設定:
従属変数:社会保障費の対GDP比
処置群:構造変化が発生した国
統制群:構造変化が発生していない国
メインの独立変数:左派政党の議席比率
満たされるべき仮定:処置が起きなかった場合の平均アウトカムの継時的変化は、処置群と対照群で一致する(平行トレンド仮定(common trend assumption))。
→[処置効果]=[処置後に観測される処置群の実際の変化]―[処置前と同じなら観測されたであろう変化]
二元変数の固定効果モデルによって、DIDは以下のように表される。
\[ Y_{it}=\alpha_i+\gamma_t+\tau_tD_{it}+\beta_1(\mathrm{leftseat}\times D_{it})+\epsilon_{it}. \]
各項のnotationは以下の通り。
\(Y_{it}\): 国\(_i\)の年\(t\)における社会保障費対GDP比
\(\alpha_i\): 国ごとの時間不変(time invariant)の固定効果
\(\gamma_{t}\): 全体の年のショック
\(D_{it}\): 構造変化後ダミー(各不況開始後=1、それより前=0)
\(\beta_1\): 左派議席率の構造変化のもとでの係数の変化(差分の差分項)
#------ データの成形
df_cws <- df_cws %>%
# ID変数とyear変数を因子化
mutate(
id_f = factor(id),
year_f = factor(year),
# AS(2004)の記述に沿って構造変化ダミーを設定
structural_break = case_when(
id == "NZL" & year > 1978 ~ 1,
id == "NZL" ~ 0,
id %in% c("AUT","BEL","DNK","FIN","SWE","GBR") & year > 1981 ~ 1,
id %in% c("AUT","BEL","DNK","FIN","SWE","GBR") ~ 0,
id %in% c("CAN","DEU","ITA","NLD","NOR","CHE","USA") & year > 1982 ~ 1,
id %in% c("CAN","DEU","ITA","NLD","NOR","CHE","USA") ~ 0,
id %in% c("AUL","FRA","IRL","JPN") & year > 1983 ~ 1,
id %in% c("AUL","FRA","IRL","JPN") ~ 0,
TRUE ~ NA_real_
),
# DIDとそれとメインの変数との交互作用項の設定
D = structural_break,
D_left = structural_break * leftseat
) %>%
filter(!is.na(D))
#------ モデルの当てはめ
model_did <- lm(
socx_pub ~
D + # τ:構造変化による切片の変化
leftseat # 構造変化前のスロープ
+ D_left # β:構造変化によるスロープ差分の差分
+ popold + vturn + singmemd + hunemr + cpi # 共変量
+ id_f + year_f, # 国・年固定効果
data = df_cws
)
#------ モデルの推定結果の解釈
coef(summary(model_did))
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -15.134395844 3.42856894 -4.41420199 1.500230e-05
## D 2.379308526 1.01711110 2.33928087 2.009650e-02
## leftseat -0.032569130 0.02238309 -1.45507750 1.468825e-01
## D_left -0.001254188 0.02015729 -0.06222009 9.504365e-01
## popold 83.918208377 8.54993926 9.81506486 1.735706e-19
## vturn 0.148101326 0.02936073 5.04419721 8.676883e-07
## singmemd 0.271207264 0.22242624 1.21931326 2.238564e-01
## hunemr 0.307168327 0.04818821 6.37434546 8.601061e-10
## cpi 0.101908097 0.01934047 5.26916217 2.926627e-07
## id_fBEL 6.776985786 0.60819985 11.14269553 9.697727e-24
## id_fCAN 4.437048133 0.92196380 4.81260557 2.558583e-06
## id_fFIN 11.930170931 0.94271114 12.65517120 8.653048e-29
## id_fFRA 12.655394316 0.92065980 13.74600514 1.615659e-32
## id_fITA 4.167613159 0.58572724 7.11527968 1.137403e-11
## id_fJPN -0.946149242 1.11457942 -0.84888454 3.967449e-01
## id_fNOR 7.495076111 0.72016016 10.40751286 2.328732e-21
## id_fNZL 5.978635938 0.45709627 13.07959905 3.110915e-30
## id_fSWE 11.934212806 0.76669915 15.56570499 8.097942e-39
## id_fUSA 3.650778329 1.36799636 2.66870470 8.104736e-03
## year_f1981 -0.515455521 0.80427810 -0.64089215 5.221706e-01
## year_f1982 -0.504894369 0.82368117 -0.61297306 5.404426e-01
## year_f1983 -1.841326055 0.95765442 -1.92274584 5.563016e-02
## year_f1984 -3.140681533 1.05515107 -2.97652311 3.196802e-03
## year_f1985 -2.721446636 1.08624509 -2.50537072 1.285896e-02
## year_f1986 -3.319707673 1.12148323 -2.96010459 3.366101e-03
## year_f1987 -3.763216620 1.15390103 -3.26129930 1.260842e-03
## year_f1988 -4.006269671 1.17644230 -3.40541111 7.677331e-04
## year_f1989 -4.443019188 1.22762819 -3.61918962 3.565878e-04
## year_f1990 -4.453359409 1.28169476 -3.47458659 6.014404e-04
## year_f1991 -4.031205852 1.33679646 -3.01557190 2.825045e-03
## year_f1992 -3.622763516 1.37560921 -2.63357028 8.967627e-03
## year_f1993 -4.026557177 1.41609777 -2.84341751 4.826300e-03
## year_f1994 -4.487723108 1.44558714 -3.10442933 2.122343e-03
## year_f1995 -4.963861112 1.47725348 -3.36019591 8.986637e-04
## year_f1996 -5.139766614 1.50604469 -3.41275836 7.482207e-04
## year_f1997 -5.547583234 1.53104078 -3.62340657 3.511046e-04
## year_f1998 -5.339600867 1.54991584 -3.44509085 6.677332e-04
## year_f1999 -5.473870324 1.56901858 -3.48872243 5.719019e-04
## year_f2000 -6.039912123 1.60249110 -3.76907686 2.037874e-04
## year_f2001 -6.111385871 1.64372087 -3.71801926 2.470706e-04
## year_f2002 -5.773398349 1.67295461 -3.45101913 6.538837e-04
## year_f2003 -5.631633735 1.70719283 -3.29876839 1.110068e-03
## year_f2004 -6.040658138 1.73920266 -3.47323419 6.043400e-04
## year_f2005 -6.538758838 1.77460300 -3.68463191 2.799178e-04
## year_f2006 -7.071467980 1.81854440 -3.88853194 1.288203e-04
## year_f2007 -7.413688769 1.85675906 -3.99281142 8.552935e-05
## year_f2008 -7.182198939 1.92632992 -3.72843657 2.375900e-04
## year_f2009 -5.481725605 1.94316626 -2.82102758 5.165120e-03
## year_f2010 -6.328417406 2.13464711 -2.96461995 3.318740e-03
現時点で、\(D\)項が5%水準で統計的に有意であることが明らか。\(D\)項の係数がDIDにおけるAverage Treatment Effect on the Treated (ATT)で、構造変化(処置)を受けた後に、社会保障費対GDP比が平均でどれだけシフトしたかを表す。いまそれが5%水準で統計的に有意となっている。
さらに、構造変化前後の結果の平均の比較を図によって示し対比させてみよう。シンプルな平均の差分の描画だが、推定結果との関係でどのようなことを読み取れるだろうか。
library(grid)
#------- 構造変化前後をもとにグループ化、そのもとでの平均と信頼区間を計算
df_summary <- df_cws %>%
group_by(D) %>%
summarise(
mean_socx = mean(socx_pub, na.rm = TRUE),
n = sum(!is.na(socx_pub)),
sd = sd(socx_pub, na.rm = TRUE),
se = sd / sqrt(n),
ci_mult = qt(0.975, df = n - 1),
lower = mean_socx - ci_mult * se,
upper = mean_socx + ci_mult * se
) %>%
#
mutate(
label = paste0(
round(mean_socx, 2), "\n[",
round(lower, 2), ", ",
round(upper, 2), "]"
)
)
#------- 図示
ggplot(df_summary, aes(x = factor(D, labels = c("前", "後")), y = mean_socx)) +
geom_col(fill = "gray70", width = 0.6) +
geom_errorbar(aes(ymin = lower, ymax = upper),
width = 0.2, size = 0.8) +
# ラベルをバーの右側に配置。hjust=0 で左揃え、nudge_x で少し右へ
geom_label(aes(label = label),
hjust = 0,
nudge_x = 0.25,
nudge_y = 0,
fill = "white",
label.r = unit(0.15, "lines"),
size = 3) +
labs(
x = "構造変化",
y = "社会保障費対GDP比 (平均)",
title = "構造変化前後の平均比較"
) +
scale_x_discrete(expand = expansion(add = c(0.5, 1.5))) +
theme_bw()
次に推定結果をもとに、左派議席率が社会保障費対GDP比に与える効果の違いを検討しよう。先の結果からも、\(\beta_1(\mathrm{leftseat}\times D_{it})\)は統計的に有意ではなく、構造変化のもとで左派議席率の影響が変化していたとは解釈できない結果になっている。これを、図示すると以下のようになる。
#----- 共変量の平均と固定効果のベースレベルを準備
cov_means <- df_cws %>% summarise(
popold = mean(popold, na.rm=TRUE),
vturn = mean(vturn, na.rm=TRUE),
singmemd = mean(singmemd, na.rm=TRUE),
hunemr = mean(hunemr, na.rm=TRUE),
cpi = mean(cpi, na.rm=TRUE)
)
id_levels <- levels(df_cws$id_f)
year_levels <- levels(df_cws$year_f)
base_id <- id_levels[1]
base_year <- year_levels[1]
#----- model_did から係数を取り出す
co <- coef(model_did)
beta0 <- co["(Intercept)"]
beta1 <- co["leftseat"]
tau <- co["D"]
delta_beta <- co["D_left"]
#----- x の範囲設定
x_min <- min(df_cws$leftseat, na.rm=TRUE)
x_max <- max(df_cws$leftseat, na.rm=TRUE)
x_seq <- seq(x_min, x_max, length.out = 50)
#----- 予測用データフレーム作成し、他の変数についても mutate() で追加
df_line <- tibble(
leftseat = rep(x_seq, 2),
period = rep(c("前","後"), each = length(x_seq))
) %>%
mutate(
D = if_else(period == "前", 0L, 1L),
D_left = D * leftseat,
# 共変量は平均値
popold = cov_means$popold,
vturn = cov_means$vturn,
singmemd = cov_means$singmemd,
hunemr = cov_means$hunemr,
cpi = cov_means$cpi,
# 固定効果因子はベースレベルかつ元の因子レベルを指定
id_f = factor(base_id, levels = id_levels),
year_f = factor(base_year, levels = year_levels)
)
#----- predict() で標準誤差を取って、直線に関する95% CI 計算
pred <- predict(model_did, newdata = df_line, se.fit = TRUE)
df_line <- df_line %>%
mutate(
fit = pred$fit,
se = pred$se.fit,
lower = fit - 1.96 * se,
upper = fit + 1.96 * se
)
#----- 図におけるyとxの最小値、最大値を設定
y_pre_min <- df_line %>% filter(period=="前", leftseat==x_min) %>% pull(fit)
y_post_min <- df_line %>% filter(period=="後", leftseat==x_min) %>% pull(fit)
y_pre_max <- df_line %>% filter(period=="前", leftseat==x_max) %>% pull(fit)
y_post_max <- df_line %>% filter(period=="後", leftseat==x_max) %>% pull(fit)
#----- 図示
ggplot(df_line, aes(x = leftseat, color = period, fill = period)) +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = 0.2, color = NA) +
geom_line(aes(y = fit), size = 1) +
# 切片の変化に関するτを矢印も含めて描画、説明を付記
geom_segment(x = x_min, xend = x_min,
y = y_pre_min, yend = y_post_min,
arrow = arrow(length = unit(0.2, "cm")),
color = "blue") +
annotate("text",
x = x_min,
y = (y_pre_min + y_post_min)/2,
label = "切片シフト (τ)",
hjust = -0.1,
color = "blue") +
# 傾きの変化に関するδを矢印も含めて描画、説明を付記
geom_segment(x = x_max, xend = x_max,
y = y_pre_max, yend = y_post_max,
arrow = arrow(length = unit(0.2, "cm")),
color = "darkgreen") +
annotate("text",
x = x_max,
y = (y_pre_max + y_post_max)/2,
label = "直線の傾きの変化 (δ)",
hjust = 1.1,
color = "darkgreen") +
scale_color_manual(values = c("前"="steelblue","後"="tomato")) +
scale_fill_manual(values = c("前"="steelblue","後"="tomato")) +
labs(
x = "左派議席率",
y = "社会保障費対GDP比(予測値)",
color = "時期",
fill = "時期",
title = "構造変化前後の回帰直線\n(左派議席率の効果への注目)"
) +
theme_bw()
ではこれらの結果は、平行トレンド仮定を満たしているのだろうか。もし構造変化が起こらなければ、処置群と対照群の個体は時間変化のもとで平行した変化を示すという平行トレンド仮定(common trend assumption)を満たさなくてはならない。仮定が満たされているかを、チェックしておく。
この検定において、
\(H_0\): 処置前の各国における年に対する回帰係数の平均は0
\(H_1\): 平均の傾きは0ではない(国ごとに異なるトレンドがある)
df_cws %>%
#各国ごとに「最初の構造変化年」を計算
group_by(id) %>%
mutate(
first_break_year = min(year[structural_break == 1], na.rm = TRUE)
) %>%
ungroup() %>%
na.omit() %>%
#その年より前だけを残す
filter(year < first_break_year) %>%
#国ごとにsocx_pub ~ year の傾きを回帰:トレンドの検出を行うため!
group_by(id) %>%
summarise(
trend = coef(lm(socx_pub ~ year, data = cur_data()))[2]
) %>%
ungroup() %>%
#傾きの平均がゼロかどうかの t-test
pull(trend) %>%
t.test(mu = 0)
##
## One Sample t-test
##
## data: .
## t = 2.87, df = 3, p-value = 0.06405
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## -0.07170661 1.38913831
## sample estimates:
## mean of x
## 0.6587159
推定結果から、構造変化前における各国の年次トレンドは0.659[-0.0717,1.3891]となっており、信頼区間はゼロをまたいでいることから、真の傾きがゼロである可能性を排除できない\(t=2.87(p-value=0.064)\)。
帰無仮説「処置前のトレンドの傾きの平均はゼロ」=平行トレンドが成り立っているを棄却できないことから、平行トレンド仮定を満たしていると解釈することになる。