2022年1月・2月号の「実証ビジネス・エコノミクス」の第6回「戦略は企業の特性が決める:静学ゲームの推定[応用編]」に関するRプログラムの紹介である。本プログラムによって誌面で紹介した分析を再現しつつ、実際に手を動かしながら学ぶことを目的とする。
本連載の内容、およびサンプルコード等の資料は、情報の提供のみを目的としていますので、運用につきましては十分にご確認をいただき、お客様ご自身の責任とご判断によって行ってください。これらの情報の運用結果により損害等が生じた場合でも、日本評論社および著者はいかなる責任を負うことはできませんので、ご留意ください。
今回のプログラムの作成に際して島本幸典さん(東京大学大学院経済学研究科)にご尽力頂きました。また、プログラムの実行確認に際して、小磯慎士さん(早稲田大学政治経済学部)にご協力頂きました。この場を借りて、御礼申し上げます。
以下で読み込んでいるパッケージについて、まだインストールしていない場合には、install.packages()
でインストールを行う。
# ワークスペースを初期化
rm(list = ls())
# パッケージを読み込む
require(tidyverse)
## Loading required package: tidyverse
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.3 v purrr 0.3.4
## v tibble 3.1.1 v dplyr 1.0.5
## v tidyr 1.1.3 v stringr 1.4.0
## v readr 1.4.0 v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
require(tictoc)
## Loading required package: tictoc
require(skimr)
## Loading required package: skimr
require(modelsummary)
## Loading required package: modelsummary
require(kableExtra)
## Loading required package: kableExtra
##
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
##
## group_rows
require(doParallel)
## Loading required package: doParallel
## Loading required package: foreach
##
## Attaching package: 'foreach'
## The following objects are masked from 'package:purrr':
##
## accumulate, when
## Loading required package: iterators
## Loading required package: parallel
require(doRNG)
## Loading required package: doRNG
## Loading required package: rngtools
# 病院データを読み込む
data_raw <- readr::read_csv(file = "data_hospital_chapter6.csv",
locale = locale(encoding = "UTF-8"))
##
## -- Column specification --------------------------------------------------------
## cols(
## .default = col_double(),
## Prefecture = col_character(),
## SecondaryAreaName = col_character(),
## Management = col_character()
## )
## i<U+00A0>Use `spec()` for the full column specifications.
# ダミー変数を作成
data_cleaned <- data_raw %>%
# 大学(公立、国立、私立大学)ダミー
dplyr::mutate(DaigakuDum = if_else(
Management %in% c('公立大学法人','国(国立大学法人)','私立学校法人'),1, 0)) %>%
# 病床数0ダミー
dplyr::mutate(ZeroBedDum = if_else(NumBeds == 0, 1, 0))
# 単位の調整と、対数変換した変数の作成
data_cleaned <- data_cleaned %>%
dplyr::mutate(
NumBeds = NumBeds/100,
LogNumBeds = log(NumBeds+0.01),
Population = Population/10^6,
Menseki = Menseki/100,
TaxableIncome = TaxableIncome/1000,
LogPop = log(Population),
LogIncome = log(TaxableIncome))
data_cleaned %>%
dplyr::group_by(MRIOwnDum) %>%
dplyr::select(MRIOwnDum, Kyukyu, Kinou, Sien, Hyoka,
DepNeurology ,DepNeurosurgery, NumBeds,
ZeroBedDum, DaigakuDum) %>%
skimr::skim() %>%
skimr::yank("numeric") %>%
dplyr::select(skim_variable, MRIOwnDum, mean, sd) %>%
as.data.frame() %>%
tidyr::pivot_wider(names_from = MRIOwnDum,
values_from = c(mean, sd)) %>%
dplyr::select(skim_variable, mean_1, sd_1, mean_0, sd_0) %>%
dplyr::mutate(skim_variable = c('救急', '機能', '支援',
'評価', '神経内科', '神経外科',
'病床数', '病床0', '大学病院')) %>%
kableExtra::kable(format = "html",
booktabs = TRUE,
caption = "記述統計",
col.names = c('変数', '平均', '標準偏差',
'平均', '標準偏差'),
digits = 3) %>%
kableExtra::add_header_above(
c(" ", "MRIを購買した病院" = 2,
"MRIを購買していない病院" = 2)) %>%
kableExtra::kable_styling()
変数 | 平均 | 標準偏差 | 平均 | 標準偏差 |
---|---|---|---|---|
救急 | 0.712 | 0.453 | 0.302 | 0.459 |
機能 | 0.020 | 0.140 | 0.003 | 0.050 |
支援 | 0.048 | 0.213 | 0.004 | 0.063 |
評価 | 0.433 | 0.496 | 0.169 | 0.375 |
神経内科 | 0.393 | 0.489 | 0.122 | 0.327 |
神経外科 | 0.570 | 0.495 | 0.082 | 0.275 |
病床数 | 2.077 | 1.941 | 0.392 | 0.711 |
病床0 | 0.058 | 0.234 | 0.453 | 0.498 |
大学病院 | 0.042 | 0.200 | 0.005 | 0.067 |
# 以下の表を作成するための関数
per_MRI_cal <- function(df){
df %>%
dplyr::summarise(Total = n(),
MRIHos = sum(MRIOwnDum),
PerMRI = round(MRIHos / Total * 100, 2))
}
# 病院のカテゴリー毎に、総病院数、MRI所有病院数、その割合を確認
list(data_cleaned,
data_cleaned %>% dplyr::filter(Kyukyu == 1),
data_cleaned %>% dplyr::filter(Sien == 1),
data_cleaned %>% dplyr::filter(Hyoka == 1),
data_cleaned %>% dplyr::filter(DepNeurology == 1),
data_cleaned %>% dplyr::filter(DepNeurosurgery == 1),
# 元データの上位25%が1.2
data_cleaned %>% dplyr::filter(LogNumBeds >= log(1.2)),
data_cleaned %>% dplyr::filter(DaigakuDum == 1),
data_cleaned %>% dplyr::filter(ZeroBedDum == 1)
) %>%
purrr::map_dfr(.f = per_MRI_cal) %>%
dplyr::mutate(Category =
c('All', 'Kyukyu', 'Sien',
'Hyoka', 'DepNeurology', 'DepNeurosurgery',
'Top25%_NumBeds', 'DaigakuDum', 'ZeroBedDum')) %>%
dplyr::relocate(Category, .before = Total) %>%
kableExtra::kable(format = "html",
booktabs = TRUE) %>%
kableExtra::kable_styling()
Category | Total | MRIHos | PerMRI |
---|---|---|---|
All | 8862 | 3365 | 37.97 |
Kyukyu | 4051 | 2392 | 59.05 |
Sien | 182 | 160 | 87.91 |
Hyoka | 2386 | 1456 | 61.02 |
DepNeurology | 1987 | 1318 | 66.33 |
DepNeurosurgery | 2365 | 1914 | 80.93 |
Top25%_NumBeds | 2264 | 1932 | 85.34 |
DaigakuDum | 165 | 140 | 84.85 |
ZeroBedDum | 2674 | 196 | 7.33 |
# Step 0: 欠損のあるデータを削除
data_processed_pre <-
data_cleaned %>%
# 必要な変数のみ抽出
dplyr::select(CityCode, Kyukyu, Kinou, Sien, Hyoka,
DepNeurology, DepNeurosurgery, LogNumBeds,
ZeroBedDum, DaigakuDum,
Menseki, LogPop, LogIncome,
MRIOwnDum) %>%
# 欠損を含む観察を削除
na.omit()
# Step 1: シミュレーションに必要な変数を作成、参入の順番に並び替え
berry_process_data <- function(df){
data_processed <-
df %>%
dplyr::arrange(CityCode) %>%
# 各病院に乱数を振る
dplyr::mutate(TieEntryOrder = runif(nrow(df))) %>%
# 自治体毎の潜在的参入病院数、観察された参入病院数の変数を作成
#(本誌のIm, Nmに対応)
dplyr::group_by(CityCode) %>%
dplyr::mutate(NumPotenHos = as.numeric(n()),
NumEntryObs = sum(MRIOwnDum)) %>%
# データを参入の順番に並び替える
# 病床数が多い順に並び替える。タイの場合は乱数を参照。
dplyr::arrange(desc(LogNumBeds), desc(TieEntryOrder),
.by_group = TRUE) %>%
# incumbents move first の仮定の場合
# dplyr::arrange(desc(MRIOwnDum), desc(LogNumBeds), desc(TieEntryOrder),
# .by_group = TRUE) %>%
# 自治体毎に、参入した順番に沿った病院idを振る
dplyr::mutate(EntryOrderId = as.numeric(1:NumPotenHos)) %>%
dplyr::ungroup()
return(data_processed)
}
# 以降の結果が変わらないように、乱数を固定
set.seed(1)
# Step 1で作成した関数の実行
data_processed <- berry_process_data(data_processed_pre)
# 不要なオブジェクトを削除
rm(data_processed_pre)
# Step 2: 潜在的参入病院数を絞ってサブサンプル
NumPotenHos_max <- 4
data_processed <- data_processed %>%
# NumEntryObs_max 以下の観察に絞る
dplyr::filter(EntryOrderId <= NumPotenHos_max) %>%
# NumEntryObs, NumPotenHos の値を更新
dplyr::group_by(CityCode) %>%
dplyr::mutate(NumEntryObs = sum(MRIOwnDum),
NumPotenHos = as.numeric(n())) %>%
dplyr::ungroup()
# 病院のカテゴリー毎に、総病院数、MRI所有病院数、その割合を確認
list(data_processed,
data_processed %>% dplyr::filter(Kyukyu == 1),
data_processed %>% dplyr::filter(Sien == 1),
data_processed %>% dplyr::filter(Hyoka == 1),
data_processed %>% dplyr::filter(DepNeurology == 1),
data_processed %>% dplyr::filter(DepNeurosurgery == 1),
# 元データの上位25%が1.2
data_processed %>% dplyr::filter(LogNumBeds >= log(1.2)),
data_processed %>% dplyr::filter(DaigakuDum == 1),
data_processed %>% dplyr::filter(ZeroBedDum == 1)
) %>%
purrr::map_dfr(.f = per_MRI_cal) %>%
dplyr::mutate(Category =
c('All', 'Kyukyu', 'Sien',
'Hyoka', 'DepNeurology', 'DepNeurosurgery',
'Top25%_NumBeds', 'DaigakuDum', 'ZeroBedDum')) %>%
dplyr::relocate(Category, .before = Total) %>%
kableExtra::kable(format = "html",
booktabs = TRUE) %>%
kableExtra::kable_styling()
Category | Total | MRIHos | PerMRI |
---|---|---|---|
All | 4112 | 2248 | 54.67 |
Kyukyu | 2477 | 1744 | 70.41 |
Sien | 151 | 141 | 93.38 |
Hyoka | 1426 | 1096 | 76.86 |
DepNeurology | 1272 | 1015 | 79.80 |
DepNeurosurgery | 1696 | 1469 | 86.62 |
Top25%_NumBeds | 1857 | 1629 | 87.72 |
DaigakuDum | 125 | 115 | 92.00 |
ZeroBedDum | 646 | 43 | 6.66 |
# Step 3: シミュレーションを行いやすくするためにデータを縦に長く加工
# データを縦にns倍する
berry_expand_data <- function(df, ns){
# 自治体の数
M <- df %>%
dplyr::select(CityCode) %>%
dplyr::n_distinct()
# 病院の数(本誌のImのmについての和に対応)
NumHos <- df %>%
dplyr::distinct(CityCode, NumPotenHos) %>%
dplyr::summarise(sum(NumPotenHos)) %>%
as.numeric()
# 誤差項をシミュレーション
u_m0 <- rnorm(M * ns, mean = 0, sd = 1)
u_mIm <- rnorm(NumHos * ns, mean = 0, sd = 1)
u_df <- df %>%
# 自治体、病院idのみのdataframeを作成
dplyr::select(CityCode, EntryOrderId) %>%
dplyr::group_by(CityCode) %>%
nest() %>%
# シミュレーション番号を振る(列数はns倍)
tidyr::crossing(s = 1:ns) %>%
# 自治体毎の誤差項の変数を作成
dplyr::mutate(u_m0) %>%
tidyr::unnest() %>%
# 病院毎の誤差項の変数を作成
dplyr::mutate(u_mIm)
# 誤差項のdataframeを元のdataframeに統合
data_expand <- df %>%
dplyr::left_join(u_df, by = c('CityCode', 'EntryOrderId'))
return(data_expand)
}
# Step 3 で作成した関数の実行
ns <- 100
data_expand <-
berry_expand_data(
data_processed,
ns)
# データとパラメターを所与としたBerry (1992) の目的関数の値を計算する関数を作成
berry_obj <- function(params, df){
# パラメター
alpha <- params[1:8]
beta <- params[9:12]
delta <- params[13]
rho <- params[14]
# 可変利潤、固定費用の作成(本誌のvとphiに対応)
df <- df %>%
dplyr::mutate(
var_profit =
beta[1] + beta[2] * Menseki +
beta[3] * LogPop + beta[4] * LogIncome +
- delta * log(EntryOrderId) + rho * u_m0,
fixed_cost =
alpha[1] * Kyukyu +
alpha[2] * Sien + alpha[3] * Hyoka +
alpha[4] * DepNeurology + alpha[5] * DepNeurosurgery +
alpha[6] * LogNumBeds + alpha[7] * ZeroBedDum +
alpha[8] * DaigakuDum +
- sqrt(1 - rho ^2) * u_mIm
)
# Berry (1992) による手法の目的関数を計算する
obj <- df %>%
# 自治体、s、病院id、について、
# 参入するかどうかの論理値型の列を作成
dplyr::mutate(EntryDecision = var_profit > fixed_cost) %>%
# 自治体、s、について、不等式を満たす企業数を取得(本誌のN*と対応)
# EntryDecision == FALSEという条件下で、EntryOrderIdの最小値はN*+1に対応するので、
# 以下のような処理でN*+1を抽出している。論理値の条件分岐は処理速度が遅いため、
# それを回避するために以下のようなコードになっている。
# 潜在的参入企業数が10^5以上の場合は正しく動作しないので注意。
dplyr::mutate(n_cand = (EntryDecision * 10^5 + 1) * EntryOrderId) %>%
dplyr::group_by(CityCode, s) %>%
dplyr::mutate(n_star = min(n_cand) - 1) %>%
ungroup() %>%
# 例外処理: 全て(EntryDecision == TRUE)となる場合は、
# n_starをNumPotenHosに置き換える
dplyr::mutate(n_star = ifelse(n_star >= 10^5, NumPotenHos, n_star)) %>%
# 自治体について、期待病院数を計算
dplyr::group_by(CityCode) %>%
dplyr::summarise(n_exp = mean(n_star)) %>%
dplyr::left_join(df %>% dplyr::distinct(CityCode, NumEntryObs),
by = 'CityCode') %>%
# 実際の参入病院数と比較し、誤差の二乗を計算
dplyr::mutate(dif = (NumEntryObs - n_exp)^2) %>%
dplyr::summarise(mean(dif)) %>%
as.numeric()
return(obj)
}
# 係数の名前
est_names <-
c('Kyukyu', 'Sien', 'Hyoka',
'DepNeurology', 'DepNeurosurgery', 'LogNumBeds',
'ZeroBedDum', 'DaigakuDum', 'Constant',
'Menseki', 'LogPop', 'LogIncome',
'delta', 'rho')
# 初期値
param_init <-
c(-0.612340533, -5.525423772, -0.505275676,
-0.32531026, -1.04162392, -0.991878025,
-3.87040966, -1.272714254, 2.684741676,
0.040555764, 0.426448612, -1.399627382,
0.990975782, 0.958075433)
# Obj: 0.372646328
以上の初期値の元で推定を行う。
tic.clearlog()
tic()
berry_result <-
optim(params <- param_init,
berry_obj,
method = "Nelder-Mead",
df = data_expand)
# rhoの符号を正しくする
berry_result$par <- berry_result$par %>%
setNames(est_names)
berry_result$par[['rho']] <- abs(berry_result$par[['rho']])
berry_result$par <- berry_result$par %>% as.numeric()
toc()
## 243.81 sec elapsed
並列計算によってブートストラップを行う。
留意点: このブートストラップ計算には比較的時間がかかる点に留意されたい。並列計算に用いるコア数やCPUによっても異なるが、筆者のラップトップ(インテル Core i7-8650U、並列計算で6コア使用)の場合約3時間、ワークステーション(インテル Core i9-10980XE、並列計算で20コア使用)の場合約40分ほどかかる。計算時間や環境などに難がある方は、以下の2つのコードチャンクをスキップした上で、再現パッケージに含まれているBootStrap_Berry_Result.rds
ファイルを用いて、以下のコードを読み進めてもらえれば幸いである。なお、RMDファイルにおける該当の2つのコードチャンクはeval = FALSE
という設定にしているため、全体を走らせた場合には評価されない(コードとして実行されない)点に注意されたい。
最初のチャンクでは、並列計算のための設定を行う。ここで変数cores
において、並列計算に使うCPUコア数を設定する。 なお、各ブートストラップサンプルにおける推定において乱数を利用するため、前と同様に乱数を固定する必要がある。しかしながら、並列計算の場合にはset.seed()
では乱数を固定することができない。ここではdoRNGパッケージにおけるregisterDoRNG()
を利用する。
# 並列計算のための設定を行う。
cores <- 20 #並列計算に使うCPUコアの個数。
registerDoParallel(cores)
registerDoRNG(12345)
並列計算によりブートストラップを行い、結果を保存する。並列計算にはforeach
を使う。
# ブートストラップの回数
iter <- 100
# 自治体の数
M <- data_processed %>%
dplyr::select(CityCode) %>%
dplyr::n_distinct()
tic()
# 繰り返し推定を行う
bs_berry_result <-
foreach::foreach (
i = 1:iter,
.export = ls(envir=parent.frame()),
.packages = 'tidyverse'
) %dopar% {
# リサンプルを行う
bs_data <- data_processed %>%
dplyr::distinct(CityCode) %>%
dplyr::slice_sample(n = M, replace = TRUE) %>%
dplyr::arrange(CityCode) %>%
dplyr::mutate(id = 1:M) %>%
dplyr::left_join(data_processed, by = 'CityCode') %>%
# 推定ではCityCodeにより自治体を判別しているため名前を修正
dplyr::rename(CityCodeOrigin = CityCode, CityCode = id)
# リサンプルしたデータを推定のために加工
bs_data_processed <-
berry_process_data(bs_data)
# 先程と同様に推定のためにデータを縦に広げる
bs_data_expand <-
berry_expand_data(
bs_data_processed,
ns)
# 先程と同様に最適化により推定
bs_berry_est <-
optim(params <- param_init,
berry_obj,
method = "Nelder-Mead",
df = bs_data_expand
)$par
# rhoの符号を正しくする
bs_berry_est <- bs_berry_est %>%
setNames(est_names)
bs_berry_est[['rho']] <- abs(bs_berry_est[['rho']])
return(bs_berry_est)
}
toc()
# 結果を保存する。
saveRDS(bs_berry_result, file = "BootStrap_Berry_Result.rds")
ブートストラップの結果に基づいて標準誤差を計算する。
bs_berry_result <- readRDS("BootStrap_Berry_Result.rds")
# 標準誤差を計算
berry_se <- bs_berry_result %>%
map_dfr(~ unlist(as.data.frame(.))) %>%
summarise(across(everything(), sd)) %>%
as.numeric()
# 推定結果
tibble(
name = c(est_names, 'Value of obj'),
BR_est = c(berry_result$par, berry_result$value),
# BR_se = berry_result_1129$BR_se
BR_se = c(berry_se, NA)
) %>%
dplyr::mutate(name = c('救急', '支援', '評価', '神経内科', '神経外科',
'log(病床数)', '病床0', '大学病院',
'定数項', '面積', 'log(人口)', 'log(課税所得)',
'delta', 'rho', '目的関数の値')) %>%
kableExtra::kable(format = "html",
booktabs = TRUE,
caption = "Berry (1992) による推定結果",
col.names = c('変数', '推定値', '標準誤差'),
digits = 3) %>%
kable_styling()
変数 | 推定値 | 標準誤差 |
---|---|---|
救急 | -0.589 | 0.065 |
支援 | -5.588 | 0.125 |
評価 | -0.497 | 0.083 |
神経内科 | -0.349 | 0.124 |
神経外科 | -1.048 | 0.105 |
log(病床数) | -0.969 | 0.042 |
病床0 | -3.230 | 0.161 |
大学病院 | -1.243 | 0.172 |
定数項 | 2.686 | 0.045 |
面積 | 0.033 | 0.014 |
log(人口) | 0.418 | 0.017 |
log(課税所得) | -1.434 | 0.030 |
delta | 0.965 | 0.060 |
rho | 0.966 | 0.033 |
目的関数の値 | 0.370 | NA |
# 各市場において病院の参入をシミュレートする関数
entry_sim <- function(df, delta){
N <- df$NumPotenHos %>% unique()
prof_vec <- df$prof_excl_comp
EntryCond <- c()
n <- 1
for (i in 1:N) {
net_prof <- prof_vec[i] - delta * log(n)
if (net_prof > 0) {
# 参入する場合
EntryCond <- c(EntryCond, 1)
n <- n + 1
} else {
# 参入しない場合
EntryCond <- c(EntryCond, 0)
}
}
return(EntryCond)
}
# 推定したパラメターを取得
berry_est <- berry_result$par
alpha <- berry_est[1:8]
beta <- berry_est[9:12]
delta <- berry_est[13]
rho <- berry_est[14]
# モデルによる参入予測確率を含めたdataframeを作成
data_predicted <- data_expand %>%
# 可変利潤、固定費用の作成(本誌のvとphiに対応)
dplyr::mutate(
var_profit =
beta[1] + beta[2] * Menseki +
beta[3] * LogPop + beta[4] * LogIncome +
rho * u_m0,
fixed_cost =
alpha[1] * Kyukyu +
alpha[2] * Sien + alpha[3] * Hyoka +
alpha[4] * DepNeurology + alpha[5] * DepNeurosurgery +
alpha[6] * LogNumBeds + alpha[7] * ZeroBedDum +
alpha[8] * DaigakuDum +
- sqrt(1 - rho ^2) * u_mIm,
# 競争の効果を除いた利潤を計算
prof_excl_comp = var_profit - fixed_cost
) %>%
# entry_sim を用いて、自治体、s毎に、企業の参入行動をシミュレート
dplyr::group_by(CityCode, s) %>%
tidyr::nest() %>%
dplyr::mutate(EntryCond = purrr::map(data, entry_sim, delta = delta)) %>%
tidyr::unnest(cols = c(data, EntryCond)) %>%
dplyr::ungroup() %>%
# 参入の予測確率を計算
dplyr::group_by(CityCode, EntryOrderId) %>%
dplyr::mutate(EntryProb = mean(EntryCond)) %>%
dplyr::ungroup() %>%
dplyr::mutate(EntryPred = ifelse(EntryProb >= 0.5 ,1 ,0)) %>%
dplyr::select(-c(s, u_m0, u_mIm, var_profit, fixed_cost, prof_excl_comp, EntryCond)) %>%
dplyr::distinct(across(everything()))
# 参入病院数毎に、自治体がいくつ観察されるかを集計
data_predicted %>%
dplyr::group_by(CityCode) %>%
dplyr::summarise(Actual = sum(MRIOwnDum),
Predict = sum(EntryPred)) %>%
tidyr::pivot_longer(cols = c('Actual', 'Predict')) %>%
ggplot2::ggplot(aes(x = value, fill = name)) +
ggplot2::geom_histogram(binwidth = 0.5,
position = "dodge") +
coord_flip() +
scale_x_reverse()
# 以下の表を作成するための関数
actual_pred_cal <- function(df){
df %>%
dplyr::summarise(Actual = sum(MRIOwnDum),
Predict = sum(EntryPred))
}
# 病院のタイプ別の観察された参入病院数と予測された参入病院数
list(data_predicted,
data_predicted %>% dplyr::filter(Kyukyu == 1),
data_predicted %>% dplyr::filter(Sien == 1),
data_predicted %>% dplyr::filter(Hyoka == 1),
data_predicted %>% dplyr::filter(DepNeurology == 1),
data_predicted %>% dplyr::filter(DepNeurosurgery == 1),
# 元データの上位25%が1.2
data_predicted %>% dplyr::filter(LogNumBeds >= log(1.2)),
data_predicted %>% dplyr::filter(DaigakuDum == 1),
data_predicted %>% dplyr::filter(ZeroBedDum == 1)
) %>%
purrr::map_dfr(.f = actual_pred_cal) %>%
dplyr::mutate(Category =
c('All', 'Kyukyu', 'Sien',
'Hyoka', 'DepNeurology', 'DepNeurosurgery',
'Top25%_NumBeds', 'DaigakuDum', 'ZeroBedDum')) %>%
tidyr::pivot_longer(cols = c('Actual', 'Predict')) %>%
ggplot2::ggplot(aes(x = Category, y = value, fill = name)) +
geom_bar(position="dodge", stat = "identity") +
coord_flip()
# theme_minimal()
# 特定の病院について平均二乗誤差を表示
tibble(
category = c('Total', 'DepNeurology', 'DepNeurosurgery', 'ZeroBedDum', 'DaigakuDum'),
mse = c(data_predicted %>%
dplyr::summarise(mean((MRIOwnDum - EntryProb) ^ 2)) %>%
as.numeric() %>% round(3),
data_predicted %>%
dplyr::filter(DepNeurology == 1) %>%
dplyr::summarise(mean((MRIOwnDum - EntryProb) ^ 2)) %>%
as.numeric() %>% round(3),
data_predicted %>%
dplyr::filter(DepNeurosurgery == 1) %>%
dplyr::summarise(mean((MRIOwnDum - EntryProb) ^ 2)) %>%
as.numeric() %>% round(3),
data_predicted %>%
dplyr::filter(ZeroBedDum == 1) %>%
dplyr::summarise(mean((MRIOwnDum - EntryProb) ^ 2)) %>%
as.numeric() %>% round(3),
data_predicted %>%
dplyr::filter(DaigakuDum == 1) %>%
dplyr::summarise(mean((MRIOwnDum - EntryProb) ^ 2)) %>%
as.numeric() %>% round(3)
)
)
# 神経内科と神経外科を持つ病院はMRIの所有を義務付けられたとする
# MRI所有義務化病院が既に参入しているとして、他の病院が参入するかどうかをシミュレート
data_cf <- data_expand %>%
dplyr::group_by(CityCode, s) %>%
# MRI所有義務化病院が先になるように並び替える
dplyr::arrange(desc(DepNeurology), desc(DepNeurosurgery),
desc(LogNumBeds), desc(TieEntryOrder),
.by_group = TRUE) %>%
# 参入する順番を更新する
dplyr::mutate(EntryOrderId = as.numeric(1:NumPotenHos)) %>%
dplyr::ungroup() %>%
# 可変利潤、固定費用の作成(本誌のvとphiに対応)
dplyr::mutate(
var_profit =
beta[1] + beta[2] * Menseki +
beta[3] * LogPop + beta[4] * LogIncome +
rho * u_m0,
fixed_cost =
alpha[1] * Kyukyu +
alpha[2] * Sien + alpha[3] * Hyoka +
alpha[4] * DepNeurology + alpha[5] * DepNeurosurgery +
alpha[6] * LogNumBeds + alpha[7] * ZeroBedDum +
alpha[8] * DaigakuDum +
- sqrt(1 - rho ^2) * u_mIm,
# 競争の効果を除いた利潤を計算
prof_excl_comp = var_profit - fixed_cost
) %>%
# MRI所有義務化病院が必ず参入するために大学病院の利潤を大きな値に置き換え
dplyr::mutate(prof_excl_comp =
ifelse(DepNeurology == 1 | DepNeurosurgery == 1
, 10^5, prof_excl_comp)) %>%
# entry_sim を用いて、自治体、s毎に、企業の参入行動をシミュレート
dplyr::group_by(CityCode, s) %>%
tidyr::nest() %>%
dplyr::mutate(EntryCond = purrr::map(data, entry_sim, delta = delta)) %>%
tidyr::unnest(cols = c(data, EntryCond)) %>%
dplyr::ungroup() %>%
# 参入の予測確率を計算
dplyr::group_by(CityCode, EntryOrderId) %>%
dplyr::mutate(EntryProb = mean(EntryCond),
EntryPred = ifelse(EntryProb >= 0.5 ,1 ,0)) %>%
dplyr::ungroup() %>%
dplyr::select(-c(s, u_m0, u_mIm, var_profit, fixed_cost, prof_excl_comp, EntryCond)) %>%
dplyr::distinct(across(everything()))
# 以下の表を作成するための関数
per_decline_cal <- function(df){
df %>%
dplyr::summarise(
Total = n(),
Actual = sum(MRIOwnDum),
Fit_Pred = sum(Fit_Pred),
CF_Pred = sum(EntryPred),
CF_Pred_am_actual = sum(MRIOwnDum * EntryPred)
)
}
data_cf <-
data_cf %>%
# 元のモデルでの参入の予測を表す変数を加える
dplyr::left_join(data_predicted %>%
dplyr::select(CityCode, TieEntryOrder, EntryPred) %>%
dplyr::rename(Fit_Pred = EntryPred),
by = c("CityCode", "TieEntryOrder")) %>%
# 政策の対象かどうかを表すダミー
dplyr::mutate(PolicyTarget =
ifelse(
(DepNeurology == 1 | DepNeurosurgery == 1) & Fit_Pred != 1,
#DepNeurology == 1 | DepNeurosurgery == 1) & MRIOwnDum != 1,
1, 0)
)
# 病院のカテゴリー毎に、総病院数(Total)、
# 元のモデルにより予測されたMRI所有病院数(Fit_Pred)、
# 反実仮想分析により予測されたMRI所有病院数(CF_Pred)、
# MRI所有病院数の変化率(PerChange)、を確認
data_cf_sub <- data_cf %>%
dplyr::filter(PolicyTarget == 0)
list(data_cf_sub,
data_cf_sub %>% dplyr::filter(Kyukyu == 1),
data_cf_sub %>% dplyr::filter(Sien == 1),
data_cf_sub %>% dplyr::filter(Hyoka == 1),
data_cf_sub %>% dplyr::filter(DepNeurology == 1),
data_cf_sub %>% dplyr::filter(DepNeurosurgery == 1),
data_cf_sub %>% dplyr::filter(!(DepNeurology == 1 | DepNeurosurgery == 1)),
# 元データの上位25%が1.2
data_cf_sub %>% dplyr::filter(LogNumBeds >= log(1.2)),
data_cf_sub %>% dplyr::filter(DaigakuDum == 1),
data_cf_sub %>% dplyr::filter(ZeroBedDum == 1)
) %>%
purrr::map_dfr(.f = per_decline_cal) %>%
dplyr::mutate(Category =
c('All', 'Kyukyu', 'Sien', 'Hyoka',
'DepNeurology', 'DepNeurosurgery',
'Not_(DepNeurology_and_DepNeurosurgery)',
'Top25%_NumBeds', 'DaigakuDum', 'ZeroBedDum')) %>%
dplyr::select(Category, Fit_Pred, CF_Pred) %>%
dplyr::mutate(PerChange = round((CF_Pred/Fit_Pred - 1) * 100, 2)) %>%
kableExtra::kable(format = "html",
booktabs = TRUE,
digits = 3) %>%
kableExtra::kable_styling()
Category | Fit_Pred | CF_Pred | PerChange |
---|---|---|---|
All | 2301 | 2255 | -2.00 |
Kyukyu | 1887 | 1861 | -1.38 |
Sien | 151 | 151 | 0.00 |
Hyoka | 1201 | 1187 | -1.17 |
DepNeurology | 1076 | 1076 | 0.00 |
DepNeurosurgery | 1619 | 1619 | 0.00 |
Not_(DepNeurology_and_DepNeurosurgery) | 429 | 383 | -10.72 |
Top25%_NumBeds | 1790 | 1769 | -1.17 |
DaigakuDum | 123 | 123 | 0.00 |
ZeroBedDum | 3 | 3 | 0.00 |