目的 ロジスティック回帰を Walker-Duncan 法により行う。 R では,glm 関数を用いて分析を行うこともできる。 (多項ロジスティックモデルは nnet パッケージにある multinom 関数, 順序ロジスティックモデルは MASS パッケージにある polr 関数。) 使用法 logistic.regression(data) 引数 data データマトリックス。最終列が従属変数(0/1データであること)。 ソース インストールは,以下の 1 行をコピーし,R コンソールにペーストする source("http://aoki2.si.gunma-u.ac.jp/R/src/logistic_regression.R", encoding="euc-jp") # 多重ロジスティックモデル(Walker-Duncan 法) logistic.regression <- function(data) # データ行列(最右端の列が従属変数) { printf <- function(fmt, ...) # 書式付き print 関数 { cat(sprintf(fmt, ...)) } diff <- function(x, m, n, coeff) # 数値微分 { mp1 <- m+1 temp <- coeff[mp1]+coeff[1:m]%*%t(x[, 1:m, drop=FALSE]) p0 <- 1/(1+exp(-temp)) p1 <- 1-p0 pp <- ifelse(x[, mp1] == 1, p1, -p0) diff1 <- numeric(mp1) diff1[mp1] <- sum(pp) diff1[1:m] <- diff1[1:m]+colSums(x[,1:m, drop=FALSE]*pp) temp <- -x[, 1:m, drop=FALSE]*p0*p1 diff2 <- matrix(0, nrow=mp1, ncol=mp1) diff2[1:m, 1:m] <- t(x[,1:m, drop=FALSE])%*%as.matrix(temp) diff2[lower.tri(diff2, diag=FALSE)] <- 0 diff2[mp1, mp1] <- -sum(p0*p1) diff2[1:m, mp1] <- diff2[1:m, mp1]+colSums(temp[,1:m, drop=FALSE]) diff2 <- diff2+t(diff2) diag(diff2) <- diag(diff2)/2 return(list(diff1=diff1, diff2=diff2)) } llh <- function(x, m, coeff) # 対数尤度 { temp <- colSums(t(x[,1:m])*coeff[1:m])+coeff[m+1] -sum(log(1+exp(ifelse(x[,m+1] == 1, -temp, temp)))) } newton.logist <- function(x, m, n, sds) # ニュートン法によるあてはめ { mp1 <- m+1 coeff0 <- numeric(mp1) coeff <- rep(1e-14, mp1) for (itr in 1:500) { temp <- diff(x, m, n, coeff) coeff0 <- solve(temp$diff2, temp$diff1) converge <- all(abs(coeff0/coeff) < 1e-10) coeff <- coeff-coeff0 if (converge) { break } } stopifnot(converge == TRUE) se <- sqrt(-diag(solve(temp$diff2))) printf("\n対数尤度 = %.14g\n", llh(x, m, coeff)) printf("\nパラメータ推定値\n\n") printf(" %14s %14s %12s %8s %14s\n", "偏回帰係数", "標準偏差", "t 値", "P 値", "標準化偏回帰係数") t <- abs(coeff[1:m]/se[1:m]) p <- pt(t, n-mp1, lower.tail=FALSE)*2 for (i in 1:m) { printf("%8s %14.7g %14.7g %12.7g %8.5f %14.7g\n", vnames[i], coeff[i], se[i], t[i], p[i], coeff[i]*sds[i]) } t <- abs(coeff[mp1]/se[mp1]) p <- pt(t, n-mp1, lower.tail=FALSE)*2 printf("%8s %14.7g %14.7g %12.7g %8.5f\n%60s%i\n\n", " 定数項", coeff[mp1], se[mp1], t, p, "t の自由度 = ", as.integer(n-mp1)) return(coeff) } fitness <- function(data, m, n, coeff) # 当てはまり具合の確認 { lambda <- coeff[1:m]%*%t(data[,1:m])+coeff[m+1] # λ pred <- 1/(1+exp(-lambda)) # リスクの予測値 y <- data[,m+1] # エンドポイント div <- round(seq(0, n-1, by=n/10),0)[-1] # 対象をほぼ同数に10区分するときのサンプル数 xs <- sort(lambda) # λをソートして div2 <- (xs[div]+xs[div+1])/2 # 分割点を求める g <- findInterval(lambda, div2)+1 # λがどの区分にはいるか群分け from <- c(min(lambda), xs[div+1]) # λの区間 [from, to] to <- c(xs[div], max(lambda)) mid <- (from+to)/2 # 区間の級中心 pred <- tapply(pred, g, sum) # エンドポイントを持つものの期待値 obs <- tapply(y, g, sum) # エンドポイントを持つものの観察値 cnt <- tapply(y, g, length) # サンプル数 valid <- as.integer(names(pred)) # 区間幅が正のもの from <- from[valid] # 調整 to <- to[valid] # 調整 table <- data.frame("以上"=from, "以下"=to, "期待値"=pred, "リスク"=pred/cnt, "観察値"=obs, "故障率"=obs/cnt, "サンプル数"=cnt) print(table) printf("%60s%s\n", "", "左の2列は,各区間のλの値(最小値と最大値)") plot(c(min(from), max(to)), c(0, max(ifelse(pred>obs, pred, obs)/cnt)), # リスク,故障率,理論曲線の描画 type="n", xlab="Lambda", ylab="Risk") for (i in 1:10) { lines(c(from[i], to[i]), rep(pred[i]/cnt[i], 2), lty=3) lines(c(from[i], to[i]), rep(obs[i]/cnt[i], 2)) } x <- seq(min(lambda), max(lambda), length.out=100) lines(x, 1/(1+exp(-x))) } # logistic 関数本体 data <- subset(data, complete.cases(data)) # 欠損値を除く n <- nrow(data) # サンプルサイズ mp1 <- ncol(data) # 列数 m <- mp1-1 # 独立変数の個数 vnames <- colnames(data) # 変数名を取り出す if (is.null(vnames)) vnames <- paste("変数", 1:mp1, sep="") # 変数名がなかったら定義する num <- table(data[, mp1]) # サンプルの内訳 printf("***** 多重ロジスティック回帰\n\n") printf("サンプルサイズ %5i\n", n) printf(" 生存(打ち切り) %5i\n", as.integer(num[1])) printf(" 死亡(故障) %5i\n", as.integer(num[2])) if (num[1] == 0 || num[2] == 0 || num[1]+num[2] == 2 || n <= mp1) { stop("有効ケース数が 1 以下です\n") } means <- colMeans(data) # 各変数の平均値 sds <- apply(data, 2, sd) # 各変数の標準偏差 printf("\n %15s %15s\n", "平均値", "標準偏差") for (i in 1:m) { printf("%8s %15.7g %15.7g\n", vnames[i], means[i], sds[i]) } coeff <- newton.logist(data, m, n, sds) # パラメータ推定 fitness(data, m, n, coeff) # 当てはまり具合の確認 invisible(coeff) # 係数のみを返す(自動的に表示はされない) } 使用例 分析に使うデータは lr.data という名前で用意されているとする(lr.data を見てみる)。 data <- read.table("lr.data", header=TRUE) logistic.regression(data) 出力結果例 > data <- read.table("lr.data", header=TRUE) > logistic.regression(data) ***** 多重ロジスティック回帰 サンプルサイズ 98 生存(打ち切り) 85 死亡(故障) 13 平均値 標準偏差 x1 132.6735 14.50473 x2 223.2347 49.22509 対数尤度 = -36.09198547355 パラメータ推定値 偏回帰係数 標準偏差 t 値 P 値 標準化偏回帰係数 x1 0.008297108 0.02120826 0.3912206 0.69651 0.1203473 x2 0.01138648 0.005739863 1.983755 0.05017 0.5605007 定数項 -5.645581 3.048239 1.852079 0.06712 t の自由度 = 95 以上 以下 期待値 リスク 観察値 故障率 サンプル数 1 -3.090951 -2.6634718 0.5402969 0.05402969 0 0.0000000 10 2 -2.661530 -2.4903799 0.7291859 0.07291859 0 0.0000000 10 3 -2.472638 -2.4368895 0.7120887 0.07912096 0 0.0000000 9 4 -2.398670 -2.2024521 0.9167646 0.09167646 3 0.3000000 10 5 -2.160525 -2.0547805 1.0976992 0.10976992 1 0.1000000 10 6 -2.029713 -1.9455944 1.2061576 0.12061576 1 0.1000000 10 7 -1.944181 -1.7427561 1.3501939 0.13501939 1 0.1000000 10 8 -1.724572 -1.5495385 1.4273797 0.15859774 3 0.3333333 9 9 -1.522529 -1.2521662 1.9860571 0.19860571 0 0.0000000 10 10 -1.238838 0.5878548 3.0341763 0.30341763 4 0.4000000 10 左の2列は,各区間のλの値(最小値と最大値)
解説ページ