多重ロジスティックモデル(Walker-Duncan 法)     Last modified: Nov 15, 2004

目的

ロジスティック回帰を 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列は,各区間のλの値(最小値と最大値)

・ 解説ページ


・ 直前のページへ戻る  ・ E-mail to Shigenobu AOKI

Made with Macintosh