皆さんこんにちは。 @Hiro_macchan です。 2014年も残すところあとわずかとなりました。いかがお過ごしでしょうか?
私は今年は進捗があまり出なかった年だったので、年末ロスタイムにいろいろ頑張りましたが、諦めて、来年は頑張ることにしました。
さて、この資料はR Advent Calendar 2014 の12/29分の資料です。
遅れた事をお詫び申し上げます。
来年の頭にちょっと仕事でmixed-effect model とか GEE とかのお勉強をしないといけない機会があります。
いずれの手法も、パネルデータやクラスターの存在するデータに対して適応する手法です。
例えば私の場合、複数の医療施設から収集した観察データを解析して因果推論を行う際に、医療機関というクラスターを無視した解析を行うと、ちょっと困ったことになるので、その部分を適切に取り扱うために利用しています。
折角の機会なんで、シミュレーションを使った統計解析の手法比較をやってみようと思い立ちました。
さて、思い立ったのはいいのですが私には疑似データ作成の経験がない。
参考資料を探したところ以下のような資料が見つかりました。
http://blog.recyclebin.jp/archives/4194#more-4194
@kos59125 さんの資料を参考にRStan 使って華麗に疑似データ生成しようと思ったんですが、やってみたらきつかったので諦めました。
資料を参考に、階層データの分析を行っているStanコードのデータブロックとパラメーターブロックを入れ替えればいいかと思ったんですが、推察するに、Stanはパラメーターブロックにint が使えないため、クラスターIDをパラメーターとしてうまく生成できないみたいです。
残念ながら、僕のStan力が低いので、この推察が正しいのか全く分かりませんが、軽い気持ちでStan に手を出すと死ぬことがわかったのはこの年末に得た大きな収穫です。
ここで筆をおこうかと思ったのですが、さすがにそれはちょっとあれなんで、以下のサイトのコードを参考に新たに人工データ作成に取り組みました。
上記のサイトでは、病院―担当医師―患者という階層のある疑似的な医療データの作成を行っています。基本的なフローは 以下の通りです。
・病院毎の担当医師数、担当医師毎の患者数を指定
・変数数の内多変量正規分布に従う変数には平均値mu と共分散行列R を指定する。そのほかに、\(\chi^2\)二乗分布に従う変数も用意するのでたぶん自由度も指定する。
・病院・医師単位の変数を作成する。ここで、正規分布に従う誤差(Hint,Dint)を入れてデータにクラスターを作成する。
・患者単位の変数を用意する。
・変数間の交互作用項を用意する。 ・病院単位変数、医師単位変数、患者単位変数をまとめて、predictor matrix(dat) に格納する。
・アウトカムとの関連をモデル化した際の、モデルパラメーターを model parameter matrix(b)に格納する。
・アウトカムを\(N(\mu,sd)\)正規分布から発生させる。正規分布のパラメータ\(mu\)はdat %*% b$outcome で指定する。
・二値のアウトカムは正規分布を切って作ってる。
では、上記の流れに沿ってデータを作っていきます。
作りたいデータは以下の通りです。
・病院単位でデータにクラスターがある。 ・病院単位・患者単位の変数を含む。 ・患者に対する治療(Treatment)は患者単位変数・病院単位変数の影響を受ける。 ・患者のアウトカム(Outcome)は患者単位変数・病院単位変数・Treatmentの影響を受ける。
require(compiler)
require(dplyr)
require(MASS)
require(tableone)
require(lme4)
require(corpcor)
require(Epi)
dmat <- cmpfun(function(i) {
#Create dummy matrix
j <- length(i)
n <- sum(i)
index <- cbind(start = cumsum(c(1, i[-j])), stop = cumsum(i))
H <- matrix(0, nrow = n, ncol = j)
for (i in 1:j) {
H[index[i, 1]:index[i, 2], i] <- 1L
}
return(H)
})
r <- cmpfun(function(n, mu, sigma, data) {
dmat(n) %*% rnorm(length(n), mu, sigma) * data
})
logit <- cmpfun(function(xb) 1/(1 + exp(-xb)))
mycut <- cmpfun(function(x, p) {
cut(x = x, breaks = quantile(x, probs = p), labels = FALSE, include.lowest = TRUE)
})
hgraph <- cmpfun(function(data) {
oldpar <- par(no.readonly = TRUE)
on.exit(par(oldpar))
n <- ncol(data)
ncol <- ceiling(sqrt(n))
nrow <- ceiling(n/ncol)
par(mfrow = c(nrow, ncol))
if (!is.null(colnames(data))) {
i <- colnames(data)
} else {
i <- seq(from = 1, to = n)
}
out <- lapply(i, function(x) {
if (is.numeric(data[, x])) {
hist(data[, x], main = x, xlab = "")
} else barplot(table(data[, x]), main = x, xlab = "", ylab = "Frequency")
})
return(invisible(out))
})
## seed for simulation parameters
# set.seed(1)
# total number of hospitals
k <- 200
# number of patients within each hospital
n <- sample(5:120, size = k, TRUE)
# total number of patients
j <- sum(n)
mu <- list(int = 0,
n_bed = 100,
n_md = 30,
cont = c(Age = 5,
Sex = 0.5,
SBP = 10),
bounded = c(LengthofStay = 14))
R <- diag(3)
rownames(R) <- names(mu$cont)
R[1, 2] <- 0.3
R[1, 3] <- 0.3
R[2, 3] <- -0.5
R[lower.tri(R)] <- t(R)[lower.tri(t(R))]
(R <- cov2cor(make.positive.definite(R)))
## [,1] [,2] [,3]
## Age 1.0 0.3 0.3
## Sex 0.3 1.0 -0.5
## SBP 0.3 -0.5 1.0
p <- list(sex = 0.4)
#Create hospital variables
## hospital variables
b <- cbind(HID = 1:k,
Hint = rnorm(k, mean = mu$int, sd = 1),
n_bed = rnorm(k, mean = mu$n_bed, sd = 1),
n_md = rnorm(k, mean = mu$n_md, sd = 1))
H <- dmat(n) %*% b
hgraph(H)
#patient level variable
#set.seed(38983)
## continuous variables
Xc <- as.data.frame(cbind(mvrnorm(n = j, mu = rep(0, 3), Sigma = R),
sapply(mu$bounded, function(k) rchisq(n = j, df = k))))
Xc <- within(Xc, {
Sex <- mycut(Sex, c(0, 1-p$sex, 1)) - 1
Age <- ((Age/1.6) + mu$cont["Age"]) * 10
SBP <- ((SBP/1.6) + mu$cont["SBP"]) * 10
})
## first few rows and histograms
head(Xc)
## Age Sex SBP LengthofStay
## 1 61.66682 1 107.39871 9.651428
## 2 41.28592 0 100.67694 17.925326
## 3 58.56566 0 105.73012 15.283480
## 4 46.75071 0 99.90775 8.533799
## 5 44.35459 0 94.79011 13.802029
## 6 50.39916 0 103.42625 21.207091
hgraph(Xc)
## create dummies and drop the intercept
## dummy はないからXc をXに変換
X <- Xc
## Final for simulation
dat <- cbind(X, H)
dat <- as.matrix(dat[, -which(colnames(dat) %in% c("HID"))])
hgraph(dat)
Treatは Treat_c とTreat_b の2種類を用意する。いずれも2値。
Treat_c は病院単位にクラスターがあるが、他の患者因子には関連がない。
Treat_b は病院単位にクラスターがあり、他の患者因子に関連している。
#Create Parameter matix
predict_name <- c("Treat_c", "Treat_b")
b <- as.data.frame(rbind(
'Age' = c(0, 3),
'Sex' = c(0, 1),
'SBP' = c(0, 4),
'LengthofStay' = c(0,0),
'Hint' = c(2, 2),
'n_bed' = c(0,0),
'n_md' = c(0,0)
))
b <- b / apply(dat, 2, sd)
colnames(b) <- predict_name
## Treatment continious
treatment <- data.frame(Treat_c = as.numeric(rnorm(n = j, mean = (dat %*% b$Treat_c), sd = 15) > quantile((dat %*% b$Treat_c), probs = .75)))
## Treatment dichotomous
treatment$Treat_b <- as.numeric(rnorm(n = j, mean = (dat %*% b$Treat_b), sd = 15) > quantile((dat %*% b$Treat_b), probs = .75))
Treatment をモデルに含めてOutcome_c, Outcome_bを用意する。 いずれも2値。
#Outcome を作る。
dat_2 <- cbind(dat,as.matrix(treatment))
hgraph(dat_2)
#Create Parameter matix 2
predict_name <- c("Outcome_c", "Outcome_b")
b_2 <- as.data.frame(rbind(
'Age' = c(1, 1),
'Sex' = c(0, 0),
'SBP' = c(2, 2),
'LengthofStay' = c(0,0),
'Hint' = c(1, 1),
'n_bed' = c(2,0),
'n_md' = c(0,2),
'Treat_c' = c(2,2),
'Treat_b' = c(-3,-3)
))
b_2 <- b_2 / apply(dat_2, 2, sd)
colnames(b_2) <- predict_name
# Outcome_c を作る。
Outcome <- data.frame(Outcome_c = as.numeric(rnorm(n = j, mean = (dat_2 %*% b_2$Outcome_c), sd = 15) > quantile((dat_2 %*% b_2$Outcome_c), probs = .75)))
## Outcome_bを作る。
Outcome$Outcome_b <- as.numeric(rnorm(n = j, mean = (dat_2 %*% b_2$Outcome_b), sd = 15) > quantile((dat_2 %*% b_2$Outcome_b), probs = .75))
## データを統合してデータセットを作成する。
finaldata <- cbind(Outcome, dat_2[,!(colnames(dat_2) %in% "Hint")],HID = H[,"HID"])
finaldata <- within(finaldata, {
Sex <- factor(Sex, labels = c("female", "male"))
HID <- factor(HID)
})
hgraph(finaldata)
Outcome_c.reg.glm<- glm(formula = Outcome_c ~ Age + SBP + Sex +Treat_b +Treat_c + n_bed + n_md + LengthofStay,family = "binomial",data=finaldata)
summary(Outcome_c.reg.glm)
##
## Call:
## glm(formula = Outcome_c ~ Age + SBP + Sex + Treat_b + Treat_c +
## n_bed + n_md + LengthofStay, family = "binomial", data = finaldata)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.7397 -1.0310 -0.8076 1.2145 2.0460
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -28.654619 1.970009 -14.545 < 2e-16 ***
## Age 0.016931 0.003277 5.167 2.38e-07 ***
## SBP 0.036530 0.003509 10.411 < 2e-16 ***
## Sexmale 0.005997 0.043105 0.139 0.889
## Treat_b -0.572769 0.037783 -15.159 < 2e-16 ***
## Treat_c 0.511271 0.035288 14.488 < 2e-16 ***
## n_bed 0.238407 0.018622 12.803 < 2e-16 ***
## n_md 0.001381 0.017737 0.078 0.938
## LengthofStay -0.004374 0.003326 -1.315 0.188
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 19036 on 13990 degrees of freedom
## Residual deviance: 18310 on 13982 degrees of freedom
## AIC: 18328
##
## Number of Fisher Scoring iterations: 4
Outcome_c.reg <- glmer(Outcome_c ~ Age + SBP + Sex +Treat_b +Treat_c + n_bed + n_md + LengthofStay + (1 | HID), data = finaldata, family = "binomial")
## Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl =
## control$checkConv, : Model failed to converge with max|grad| = 0.0380178
## (tol = 0.001, component 2)
## Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, : Model is nearly unidentifiable: very large eigenvalue
## - Rescale variables?;Model is nearly unidentifiable: large eigenvalue ratio
## - Rescale variables?
summary(Outcome_c.reg, correlation=FALSE)
## Generalized linear mixed model fit by maximum likelihood (Laplace
## Approximation) [glmerMod]
## Family: binomial ( logit )
## Formula: Outcome_c ~ Age + SBP + Sex + Treat_b + Treat_c + n_bed + n_md +
## LengthofStay + (1 | HID)
## Data: finaldata
##
## AIC BIC logLik deviance df.resid
## 18326.3 18401.8 -9153.2 18306.3 13981
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -1.8466 -0.8358 -0.6192 1.0397 2.6835
##
## Random effects:
## Groups Name Variance Std.Dev.
## HID (Intercept) 0.01158 0.1076
## Number of obs: 13991, groups: HID, 200
##
## Fixed effects:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -28.640979 2.094824 -13.672 < 2e-16 ***
## Age 0.017021 0.003284 5.183 2.19e-07 ***
## SBP 0.036523 0.003513 10.396 < 2e-16 ***
## Sexmale 0.004572 0.043192 0.106 0.916
## Treat_b -0.577380 0.037985 -15.200 < 2e-16 ***
## Treat_c 0.507775 0.035440 14.328 < 2e-16 ***
## n_bed 0.238149 0.019848 11.999 < 2e-16 ***
## n_md 0.001722 0.019524 0.088 0.930
## LengthofStay -0.004324 0.003334 -1.297 0.195
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of fixed effects could have been required in summary()
##
## Correlation of Fixed Effects:
## (Intr) Age SBP Sexmal Tret_b Tret_c n_bed n_md
## Age -0.027
## SBP -0.153 -0.396
## Sexmale -0.052 -0.400 0.506
## Treat_b 0.074 -0.135 -0.182 -0.040
## Treat_c -0.017 0.014 0.024 -0.001 -0.049
## n_bed -0.947 0.021 0.013 -0.010 -0.041 0.002
## n_md -0.266 -0.003 0.003 -0.001 -0.001 0.011 -0.015
## LengthofSty -0.027 -0.009 0.005 0.008 -0.010 -0.013 0.005 0.001
Outcome_b.reg.glm<- glm(formula = Outcome_b ~ Age + SBP + Sex +Treat_b +Treat_c + n_bed + n_md + LengthofStay,family = "binomial",data=finaldata)
summary(Outcome_b.reg.glm)
##
## Call:
## glm(formula = Outcome_b ~ Age + SBP + Sex + Treat_b + Treat_c +
## n_bed + n_md + LengthofStay, family = "binomial", data = finaldata)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.6601 -1.0479 -0.8222 1.2198 1.9375
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -14.100759 1.940763 -7.266 3.71e-13 ***
## Age 0.017149 0.003261 5.258 1.45e-07 ***
## SBP 0.033343 0.003489 9.558 < 2e-16 ***
## Sexmale -0.015074 0.042892 -0.351 0.725
## Treat_b -0.563594 0.037563 -15.004 < 2e-16 ***
## Treat_c 0.424680 0.035166 12.077 < 2e-16 ***
## n_bed 0.021204 0.018386 1.153 0.249
## n_md 0.251784 0.017848 14.107 < 2e-16 ***
## LengthofStay -0.001282 0.003307 -0.388 0.698
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 19107 on 13990 degrees of freedom
## Residual deviance: 18431 on 13982 degrees of freedom
## AIC: 18449
##
## Number of Fisher Scoring iterations: 4
Outcome_b.reg <- glmer(Outcome_b ~ Age + SBP + Sex +Treat_b +Treat_c + n_bed + n_md + LengthofStay + (1 | HID), data = finaldata, family = "binomial")
## Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl =
## control$checkConv, : Model failed to converge with max|grad| = 0.00402477
## (tol = 0.001, component 2)
## Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, : Model is nearly unidentifiable: very large eigenvalue
## - Rescale variables?;Model is nearly unidentifiable: large eigenvalue ratio
## - Rescale variables?
summary(Outcome_b.reg, correlation=FALSE)
## Generalized linear mixed model fit by maximum likelihood (Laplace
## Approximation) [glmerMod]
## Family: binomial ( logit )
## Formula: Outcome_b ~ Age + SBP + Sex + Treat_b + Treat_c + n_bed + n_md +
## LengthofStay + (1 | HID)
## Data: finaldata
##
## AIC BIC logLik deviance df.resid
## 18447.4 18522.8 -9213.7 18427.4 13981
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -1.7644 -0.8529 -0.6301 1.0462 2.3367
##
## Random effects:
## Groups Name Variance Std.Dev.
## HID (Intercept) 0.01206 0.1098
## Number of obs: 13991, groups: HID, 200
##
## Fixed effects:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -14.136912 2.068630 -6.834 8.26e-12 ***
## Age 0.017217 0.003269 5.267 1.39e-07 ***
## SBP 0.033477 0.003494 9.582 < 2e-16 ***
## Sexmale -0.015206 0.042978 -0.354 0.723
## Treat_b -0.568056 0.037752 -15.047 < 2e-16 ***
## Treat_c 0.422351 0.035295 11.966 < 2e-16 ***
## n_bed 0.020837 0.019650 1.060 0.289
## n_md 0.253765 0.019722 12.867 < 2e-16 ***
## LengthofStay -0.001233 0.003315 -0.372 0.710
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of fixed effects could have been required in summary()
##
## Correlation of Fixed Effects:
## (Intr) Age SBP Sexmal Tret_b Tret_c n_bed n_md
## Age -0.022
## SBP -0.144 -0.397
## Sexmale -0.051 -0.400 0.505
## Treat_b 0.061 -0.135 -0.181 -0.040
## Treat_c -0.002 0.013 0.018 -0.003 -0.044
## n_bed -0.944 0.013 -0.003 -0.010 -0.018 -0.019
## n_md -0.269 0.007 0.020 -0.002 -0.030 0.031 -0.022
## LengthofSty -0.029 -0.008 0.007 0.009 -0.012 -0.011 0.006 0.000
とりあえず、データの生成は何とかなりました。
ちょっと普通のLogistic モデルとfixed-effect model の違いが見づらいですが、 データをたくさん作って推計値の分布を見てみれば特性が見えるかなぁと思います。
正月はこれを使っていくつかやりたいシミュレーションをやってみたいと思います。
来年は頑張ろう。
そんなわけで、皆さん今年も御世話になりました。来年もよろしくお願いします。