やること
e-statの令和元年度国民生活基礎調査に基づいて、世帯収入のダミーデータを生む関数を作ります。
source: e-stat
なお、単身世帯・性別等の個別化された世帯収入ではなく、平均的な日本世帯収入を生むだけです。 個人の世帯収入として扱うのは不十分だと思いますが、データ遊びする分には十分遊べるかと思います。
関数の内容は以下の通りです。
- ID, sex, income (confounder), x (binary exposure), y (binary outcome) を含むデータを作成する
- incomeは交絡: incomeが少ないほどx and yが発生しやすい (i.e., confounding bias away from the null)
- 引数: n_sample=人数、risk_ratio=曝露によるリスク比
関数
関数の作成
::p_load(tidyverse)
pacman# incomeデータを国民生活基礎調査に基づいて作成
<- function(){
MakeIncomeData <- c(1:25)
income_cat <- c(0, 500000, 1000000, 1500000, 2000000, 2500000, 3000000, 3500000,
min_income 4000000, 4500000, 5000000, 5500000, 6000000, 6500000, 7000000, 7500000,
8000000, 8500000, 9000000, 9500000, 10000000, 11000000, 12000000, 15000000, 20000000)
<- c(499999, 999999, 1499999, 1999999, 2499999, 2999999, 3499999, 3999999,
max_income 4499999, 4999999, 5499999, 5999999, 6499999, 6999999, 7499999, 7999999, 8499999,
8999999, 9499999, 9999999, 10999999, 11999999, 14999999, 19999999, 100000000)
<- data.frame(income_cat = income_cat,
df_income min_income = min_income,
max_income = max_income)
return(df_income)
}
# ID, sex, income (confounder), x (binary exposure), y (binary outcome) を含むデータを作成する
# incomeが少ないほどx and yが発生しやすい-> confounding bias away from the null
# 引数: n_sample=人数、risk_ratio=曝露によるリスク比
<- function(n_sample=1000, risk_ratio=1.5){
MakeData set.seed(1234)
# income data & income_threshold
<- MakeIncomeData()
income_cat <- c(0, 0.012, 0.064, 0.127, 0.19, 0.259, 0.326, 0.397, 0.454,
income_threshold 0.51, 0.559, 0.608, 0.646, 0.692, 0.726, 0.759, 0.788,
0.814, 0.837, 0.859, 0.878, 0.909, 0.928, 0.966, 0.987, 1)
# make data frame
<- data.frame(ID = 1:n_sample)
df <- df %>%
df mutate(sex = rbinom(nrow(df), 1, 0.5)) %>%
mutate(prob_income = runif(n_sample, min=0, max=1)) %>%
mutate(income_cat = cut(prob_income,
breaks = income_threshold,
right = TRUE,
include.lowest = TRUE,
labels = c(1:25)
%>%
)) mutate(income_cat = as.integer(income_cat))
<- left_join(df, income_cat, by = "income_cat") %>%
df select(ID, sex, income_cat, min_income, max_income) %>%
mutate(income = runif(n_sample, min = min_income, max = max_income)) %>%
select(ID, sex, income)
<- df %>%
df mutate(income_cat = case_when(
< 2500000 ~ "<250",
income >= 2500000 & income < 4000000 ~ "<400",
income >= 4000000 & income < 7000000 ~ "<700",
income >= 7000000 ~ "≥700"
income
),income_cat = factor(income_cat, levels = c("<250", "<400", "<700", "≥700")))
# 曝露の発生確率と曝露(0/1)を定義
<- df %>%
df mutate(x_prob = case_when(
< 2500000 ~ 0.25,
income >= 2500000 & income < 4000000 ~ 0.2,
income >= 4000000 & income < 7000000 ~ 0.15,
income >= 7000000 ~ 0.1
income %>%
)) mutate(x = rbinom(nrow(df), 1, x_prob)) # 定めた曝露発生確率の二項分布に従って曝露を決定
# 曝露しているとイベント発生確率がrisk_ratio倍になる
<- df %>%
df mutate(y_prob = case_when(
< 2500000 ~ 0.25 * (1 + x*(risk_ratio-1)),
income >= 2500000 & income < 4000000 ~ 0.2 * (1 + x*(risk_ratio-1)),
income >= 4000000 & income < 7000000 ~ 0.15 * (1 + x*(risk_ratio-1)),
income >= 7000000 ~ 0.1 * (1 + x*(risk_ratio-1))
income %>%
)) mutate(y = rbinom(nrow(df), 1, y_prob)) %>%
select(-x_prob, -y_prob)
return(df)
}
関数の確認
使い方はこんな感じ
<- MakeData(n_sample = 1000, risk_ratio = 2.0)
df head(df)
## ID sex income income_cat x y
## 1 1 0 9057019 ≥700 1 0
## 2 2 1 4096104 <700 0 0
## 3 3 1 1309235 <250 0 0
## 4 4 1 3299529 <400 0 0
## 5 5 1 7530942 ≥700 0 0
## 6 6 1 3349626 <400 0 1
所得の分布はこの通り。200-300万円程度でピークが来ていて、右に長い裾を引くという日本世帯収入の特徴をつかめていると思います。
<- mean(df$income)
mean_income <- median(df$income)
median_income
<- paste0("mean: ", as.character(round((mean_income/1000000), digits=2)), " million yen")
mean_text <- paste0("median: ", as.character(round((median_income/1000000), digits=2)), " million yen")
median_text
<- paste0(median_text, " (orange), ", mean_text, " (skyblue)")
subtitle
ggplot(data = df, aes(x = income)) +
geom_histogram() +
geom_vline(xintercept = mean_income, linetype = 2, colour = "skyblue") +
geom_vline(xintercept = median_income, linetype = 2, colour = "orange") +
theme_bw() +
xlim(c(0, 20000000)) +
ggtitle("simulated distribution of income", subtitle)
実際の分布は次のとおりです (source: URL)。
国民生活基礎調査をもとにデータをシミュレーションしているから当然なんだけど、いいデモデータになるでしょ?
一応、xとyとの関連も確認。 単純にxとyをlog-binomialで回帰分析したモデルと、収入を4カテゴリ (250万円未満 / 250-400万円 / 400-700万円 / 700万円以上) に分けて調整したモデルの2つを実行します。
<- glm(y ~ x, data = df, family = binomial(link = "log"))
crude
summary(crude)
##
## Call:
## glm(formula = y ~ x, family = binomial(link = "log"), data = df)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.0579 -0.6398 -0.6398 -0.6398 1.8368
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.68688 0.07274 -23.19 < 2e-16 ***
## x 0.83958 0.11501 7.30 2.88e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1068.8 on 999 degrees of freedom
## Residual deviance: 1026.6 on 998 degrees of freedom
## AIC: 1030.6
##
## Number of Fisher Scoring iterations: 6
exp(coef(crude)) # risk ratio
## (Intercept) x
## 0.1850962 2.3153989
<- glm(y ~ x + income_cat, data = df, family = binomial(link = "log"))
adjusted
summary(adjusted)
##
## Call:
## glm(formula = y ~ x + income_cat, family = binomial(link = "log"),
## data = df)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.2228 -0.7399 -0.6391 -0.5278 2.0200
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.4293 0.1049 -13.628 < 2e-16 ***
## x 0.7879 0.1149 6.856 7.08e-12 ***
## income_cat<400 -0.2383 0.1557 -1.531 0.1259
## income_cat<700 -0.2596 0.1400 -1.855 0.0636 .
## income_cat≥700 -0.6109 0.1755 -3.481 0.0005 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1068.8 on 999 degrees of freedom
## Residual deviance: 1012.8 on 995 degrees of freedom
## AIC: 1022.8
##
## Number of Fisher Scoring iterations: 6
exp(coef(adjusted)) # risk ratio
## (Intercept) x income_cat<400 income_cat<700 income_cat≥700
## 0.2394715 2.1987082 0.7879789 0.7713427 0.5428814
exp(confint(adjusted)) # 95%CI
## 2.5 % 97.5 %
## (Intercept) 0.1934482 0.2914440
## x 1.7452555 2.7416437
## income_cat<400 0.5720032 1.0622755
## income_cat<700 0.5838765 1.0102595
## income_cat≥700 0.3800548 0.7554868
risk ratioは2.0で設定したところ、データではRR=2.20 になっていました。 おそらく想定した通りにちゃんと動いているでしょう。
また、交絡に関して確認すると、 crudeだと RR=2.32 で、収入を調整したadjustedだと RR=2.20 となっていました。 したがって、収入がbias away from the nullになっていることも確認できました。
実際に使う場合のtips
実際にこの関数を動かすときは、関数を.Rで作成して保存し、解析コードの.R/.Rmdから読み出すといいでしょう。
例として、以下のディレクトリ構造でコードを書いていたとします。
root directory/
├ index.Rmd (← 今書いてるドキュメント)
└ index_files/ (sub directory)
└ makedata.R
こういう構造の場合、以下のコードで.Rをソースコードとして読むことが可能です。
source("./index_files/makedata.R") # 今回使う自作関数をまとめた.Rファイル
そんじゃあね。