統計ER

R, EZR, SPSS, KH Coder を使ったデータ分析方法を紹介するブログ。ニッチな内容が多め

ログランク検定を R で行う方法

ログランク検定とは、生存時間解析で、二群以上のグループがある場合に、グループ間で統計学的に差があるかを検討する方法。

R での方法を解説。

>>もう統計で悩むのを終わりにしませんか?


↑1万人以上の医療従事者が購読中

R でログランク検定をする場合の関数は?

R でログランク検定をする場合、survival パッケージのsurvdiff()関数を使う。

survival パッケージは最初からインストールされているパッケージで、使うときにlibrary()で呼び出す。

library(survival)

survdiff()はどうやって使う?

survdiff()は、Surv()と組み合わせて使うことになる。

Surv()は、生存時間データとエンドポイントデータを使って、生存時間解析に使えるデータに変換する関数。

Surv()については、こちらも参照。

toukeier.hatenablog.com

survdiff(Surv(時間データ, イベントデータ) ~ グループ変数)

という形が基本形。

survival パッケージの ovarian データを使って、ログランク検定をしてみる。

ovarian データセットは、卵巣がんの治療を、二種類の治療法にランダムに振り分けた時の試験結果データ。

futimeが生存時間データ、fustatが打ち切りか、死亡か、rxは二つの治療法のどちらを受けたかを表す変数。

>>もう統計で悩むのを終わりにしませんか?


↑1万人以上の医療従事者が購読中

ovarian データセットの内容

ovarian データセットの先頭部分を表示してみる。以下のようなデータであることがわかる。futimeは日数。

> head(ovarian)
  futime fustat     age resid.ds rx ecog.ps
1     59      1 72.3315        2  1       1
2    115      1 74.4932        2  1       1
3    156      1 66.4658        2  1       2
4    421      0 53.3644        2  2       1
5    431      1 50.3397        2  1       1
6    448      0 56.4301        1  1       2

生存時間データとイベントデータをSurv()してみる

まずSurv()をしてみる。

> Surv(ovarian$futime,ovarian$fustat)
 [1]   59   115   156   421+  431   448+  464   475   477+  563 
[11]  638   744+  769+  770+  803+  855+ 1040+ 1106+ 1129+ 1206+
[21] 1227+  268   329   353   365   377+

数値は生存日数で、+がついている日数は観察打ち切りの日数である。

その日数までは生きていたことはわかるが、それ以降の情報がないという意味になる。

これを打ち切り(censoring、censored、センサリング、センサード)データと呼ぶ。

survfit()でグループごとのカプランマイヤー曲線を描いてみる

検定の前にカプランマイヤー曲線で図示してみる。

二種類の治療でどのくらい異なるのか、生存曲線を書いてみる。

survfit1 <- survfit(Surv(futime,fustat)~rx, data=ovarian)
plot(survfit1,las=1,xlab="Survival Time (days)", ylab="Overall Survival", col=2:3)
legend("topright", paste("Treatment=",1:2,sep=""), lty=1, col=2:3)

Treatment1よりも2のほうが生存確率が高いことがわかる。

統計学的に有意な差があるのかどうか気になるところだ。

survdiff()でログランク検定をしてみる

survdiff()を使って、ログランク検定をしてみる。

> survdiff(Surv(futime,fustat) ~ rx, data=ovarian)
Call:
survdiff(formula = Surv(futime, fustat) ~ rx, data = ovarian)

      N Observed Expected (O-E)^2/E (O-E)^2/V
rx=1 13        7     5.23     0.596      1.06
rx=2 13        5     6.77     0.461      1.06

 Chisq= 1.1  on 1 degrees of freedom, p= 0.3 

結果として、p=0.3で、統計学的有意ではない。

サンプルサイズの問題か、effect sizeの問題かは不明だが、カプランマイヤー曲線では差があるように見えるだけに惜しい気がする。

三群以上のログランク検定

ところで、survival パッケージの肺がんデータでECOG performance score(四群)の差を検定してみると以下のようになる。

肺がんデータのカプランマイヤー曲線についてはこちらを参照。

toukeier.hatenablog.com

> survdiff(Surv(time,status)~ph.ecog, data=lung)
Call:
survdiff(formula = Surv(time, status) ~ ph.ecog, data = lung)

n=227, 1 observation deleted due to missingness.

            N Observed Expected (O-E)^2/E (O-E)^2/V
ph.ecog=0  63       37   54.153    5.4331    8.2119
ph.ecog=1 113       82   83.528    0.0279    0.0573
ph.ecog=2  50       44   26.147   12.1893   14.6491
ph.ecog=3   1        1    0.172    3.9733    4.0040

 Chisq= 22  on 3 degrees of freedom, p= 7e-05 

結果として、 p = 7 \times 10^{-5} = 0.00007なので、統計学的有意にグループ間で異なっていた。

ログランク検定をStep by stepでやってみる

survdiff() 関数を使わずにログランク検定をしてみるとどうなるか?

上述の ovarian データを使ってやってみた。

ovarian データを加工する

生存時間データ、イベントデータ、治療群データの三変数に絞り、生存時間が短い順に並べ替える。

治療群1と2にわけたデータも作成する。

dat <- ovarian[,c(1,2,5)]
dat0 <- dat[order(dat$futime), ]
dat1 <- subset(dat0, dat0$rx==1)
dat2 <- subset(dat0, dat0$rx==2)

観察・期待・at risk人数を計算する

各群のイベント観察データ(人数)をO1j、O2jとする。

各群の「イベントが起きた時点」の「イベントが起きうる(at risk)」人数をN1j、N2jとする。

各群の「イベントが起きた時点」のイベントの期待値をE1j、E2jとする。

イベント観察データ(O)の加工

O1jとO2jのそれぞれの合計をO1、O2とする。

O1は治療群1のイベント発現人数で7人、O2は治療群2のイベント発現人数で5人である。

O1j <- ifelse(dat0$fustat==1 & dat0$rx==1,1,0)
O2j <- ifelse(dat0$fustat==1 & dat0$rx==2,1,0)

O1 <- sum(O1j)
O2 <- sum(O2j)
At risk 人数データ(N)の加工

At riskの人数は、最初は全員だが、イベントが発現した後は、イベント発現者は除かれていく。

人数はだんだん減っていく。

N1j <- rep(0,length(dat0$fustat))
N2j <- rep(0,length(dat0$fustat))

N1j[1] <- length(dat1$fustat)
N2j[1] <- length(dat2$fustat)

for (j in 2:length(dat0$fustat)){
    ifelse(dat0$rx[j-1]==1, N1j[j] <- N1j[j-1]-1, N1j[j] <- N1j[j-1])
    ifelse(dat0$rx[j-1]==2, N2j[j] <- N2j[j-1]-1, N2j[j] <- N2j[j-1])
}
イベント期待値データ(E)の計算

イベントが発現した時点における、各群のat risk 人数の、全体 at risk 人数に対する割合に合わせて、期待されるイベント数が計算できる。

Oj <- O1j + O2j
Nj <- N1j + N2j

E1j <- Oj*N1j/Nj 
E2j <- Oj*N2j/Nj

E1 <- sum(E1j)
E2 <- sum(E2j)

期待値の合計は治療群1で5.23人、治療群2で6.77人と計算され、survdiff()の結果と一致する。

治療群1の観察値O1が7人、治療群2の観察値O2が5人であるので、少しずつずれているのがわかる。

> E1
[1] 5.233531
> E2
[1] 6.766469
加工・計算した出来上がりのデータセット

上記で作成したデータを並べてみると以下のようになる。

> cbind(dat0,O1j,O2j,Oj,N1j,N2j,Nj,E1j,E2j)
   futime fustat rx O1j O2j Oj N1j N2j Nj       E1j       E2j
1      59      1  1   1   0  1  13  13 26 0.5000000 0.5000000
2     115      1  1   1   0  1  12  13 25 0.4800000 0.5200000
3     156      1  1   1   0  1  11  13 24 0.4583333 0.5416667
22    268      1  1   1   0  1  10  13 23 0.4347826 0.5652174
23    329      1  1   1   0  1   9  13 22 0.4090909 0.5909091
24    353      1  2   0   1  1   8  13 21 0.3809524 0.6190476
25    365      1  2   0   1  1   8  12 20 0.4000000 0.6000000
26    377      0  2   0   0  0   8  11 19 0.0000000 0.0000000
4     421      0  2   0   0  0   8  10 18 0.0000000 0.0000000
5     431      1  1   1   0  1   8   9 17 0.4705882 0.5294118
6     448      0  1   0   0  0   7   9 16 0.0000000 0.0000000
7     464      1  2   0   1  1   6   9 15 0.4000000 0.6000000
8     475      1  2   0   1  1   6   8 14 0.4285714 0.5714286
9     477      0  1   0   0  0   6   7 13 0.0000000 0.0000000
10    563      1  2   0   1  1   5   7 12 0.4166667 0.5833333
11    638      1  1   1   0  1   5   6 11 0.4545455 0.5454545
12    744      0  2   0   0  0   4   6 10 0.0000000 0.0000000
13    769      0  2   0   0  0   4   5  9 0.0000000 0.0000000
14    770      0  2   0   0  0   4   4  8 0.0000000 0.0000000
15    803      0  1   0   0  0   4   3  7 0.0000000 0.0000000
16    855      0  1   0   0  0   3   3  6 0.0000000 0.0000000
17   1040      0  1   0   0  0   2   3  5 0.0000000 0.0000000
18   1106      0  1   0   0  0   1   3  4 0.0000000 0.0000000
19   1129      0  2   0   0  0   0   3  3 0.0000000 0.0000000
20   1206      0  2   0   0  0   0   2  2 0.0000000 0.0000000
21   1227      0  2   0   0  0   0   1  1 0.0000000 0.0000000

検定統計量 カイ二乗値と有意確率の計算

検定統計量は、観察値と期待値の差の2乗を期待値で割った値を各群で求め、合計する。

(O1-E1)^2/E1 + (O2-E2)^2/E2

計算結果のカイ二乗 \chi^{2}はsurvdiff()の結果と一致して約1.1となった。

> (O1-E1)^2/E1 + (O2-E2)^2/E2
[1] 1.057393

自由度1で検定すると、上側有意確率は約0.3となる。これもsurvdiff()の結果と一致した。

> pchisq(1.057393,1,lower.tail=FALSE)
[1] 0.3038105

まとめ

Rで、二群以上の生存について統計学的に異なるかどうかを検定するログランク検定を行ってみた。

ログランク検定は、カイ二乗値を使った検定である。

ログランク検定をsurvdiff()を使わずにStep by stepでやってみると、実際にどんな計算をしているかがわかる。

イベントが起きた時点の観察値、at risk 人数から、イベントの期待値を計算し、 \chi^{2}を計算する。

検定は自由度1の \chi^{2} 検定だ。

観察値が期待値に比べて大きく離れた場合、 \chi^{2}値が大きくなり、統計学的有意と判断できる。

つまり、群間に統計学的に有意な差があるという結論になる。