引入
什麼是生存分析
生存分析是對生存資料的分析。所謂生存資料是指描述壽命或者一個發生時間的數據。通過對某一具有相同特質的羣體進行數據分析,我們可以得到這類人活過一定時間的概率。這就叫做生存分析。一個人的生存時間長短與許多因素有關,研究各個因素與生存時間有無關係以及關聯程度大小,這也是生存分析。推廣來說,疾病的復發可以看做“健康期”的生存,機器的故障可以看做“機器”的生存,甚至下崗職工再就業可以看做“失業期”的生存,生存分析可以被推廣至很多問題的分析中去。
應用場景
生存分析最早在生物醫學中使用的最多,用來預估某個羣體的存活時間,後來被推廣到了更廣泛的領域。一個做生物的專家可能通常會關心這樣一個問題:這個羣體的樣本能活多久?這個問題我們通常會使用生存分析來回答。 這個羣體可以理解爲某個國家的人民,或者注射過某種藥物的一些病人。
同樣也可以推廣到更一般的場景,公司的客戶流失情況,一個用戶開始接受他們的服務可以認爲是一個樣本的出生,一個客戶的離開可以認爲是一個樣本的死亡。用戶流失情況分析等問題很多電商平臺也十分關注。
數據截斷
以樣本的出生和死亡爲例,我們觀測一組樣本的生存時間。然而我們觀測和追蹤他們是需要時間和金錢的成本的,不可能因爲樣本一直存活着,我們就一直將某個實驗做下去。通常,專家只會對一組樣本觀測一段時間。
同樣也存在左側截斷和中間截斷
一個常見的錯誤是人們可能在評估目標羣體存活時間時會簡單的忽略右側截斷
考慮一種情況,其中人口實際上由兩個亞羣組成, 𝐴 和 𝐵。人口𝐴 壽命非常短,例如平均2個月,人口 𝐵 享有更長的使用壽命,例如平均12個月。我們事先不知道這種區別。假定我們的實驗進行了十個月,即在𝑡=10就結束了,我們希望調查整個人口的平均壽命。
在下圖中,紅線表示觀察到死亡事件的個體的壽命,藍線表示右刪失個體的壽命(未觀察到死亡)。如果要求我們估計人口的平均壽命,而我們天真地決定不包括經過權利審查的個人,那麼很明顯,我們將嚴重低估真實的平均壽命。
from lifelines.plotting import plot_lifetimes
import numpy as np
from numpy.random import uniform, exponential
N = 25
CURRENT_TIME = 10
actual_lifetimes = np.array([
exponential(12) if (uniform() < 0.5) else exponential(2) for i in range(N)
])
observed_lifetimes = np.minimum(actual_lifetimes, CURRENT_TIME)
death_observed = actual_lifetimes < CURRENT_TIME
ax = plot_lifetimes(observed_lifetimes, event_observed=death_observed)
ax.set_xlim(0, 25)
ax.vlines(10, 0, 30, lw=2, linestyles='--')
ax.set_xlabel("time")
ax.set_title("Births and deaths of our population, at $t=10$")
print("Observed lifetimes at time %d:\n" % (CURRENT_TIME), observed_lifetimes)
上面我們設定了最大值等於CURRENT_TIME=10,所以我們直觀查了10個月的數據,後面沒有發生結束事件的我們並不知道其真實的存活時長。
而真實的數據其實是下面這樣的
ax = plot_lifetimes(actual_lifetimes, event_observed=death_observed)
ax.vlines(10, 0, 30, lw=2, linestyles='--')
ax.set_xlim(0, 25)
如果我們不刪除發生右側截斷的樣本而直接求平均值,仍然會低估這個羣體的平均生存時間,要注意的是,我們只能觀測到10時刻之前的事情,10時刻之後的我們並不能看到。
生存分析最初被設計出來的目的就是爲了處理存在右側截斷的這種數據的。但是,即使我們的數據不包含右側截斷,生存分析仍然是一個強大有效的工具。
上述例子是爲了闡述方便所以讓所有的樣本的出生從0時刻開始,而實際上這是不必要的,生存分析並不要求所有樣本在同一時刻出生,它只關注每個樣本從出生到death觸發或到實驗結束之間的間隔時間,每個樣本隨時可以出生。
基本對象
接下來,我們介紹生存分析中的三個基本對象: 生存函數,風險函數和累積風險函數。
生存函數
設𝑇取自研究人羣的(可能是無限的,但總是非負的)隨機壽命。例如,一對夫婦結婚的時間。或用戶進入網頁所花費的時間(如果從不輸入,則爲無限時間)。生存函數的被定義爲:
解釋:描述了這個羣體的樣本生存時間大於t的概率,換句話說,直到時刻t,我們仍然沒有觀測到death事件的發生。
它有以下幾個性質:
- ,是隨機變量T的累積分佈函數。
- 是一個單調不增的函數。
一個生存函數的示例:
風險函數
我們同樣可能會關心某個樣本在t時刻死亡的概率,也就是說,直到t時刻我們都沒有觀察到該樣本的死亡,而在t時刻的一個右鄰域觀察到該樣本死亡。即:
由於上述極限隨着的減小而趨於0,我們定義
可以證明這等於:
可以運用貝葉斯公式
分離變量後,兩遍同時取積分,並注意到
即可以得到
累計風險函數
那麼又可以寫爲
上面的方程式,定義了所有生存函數。請注意,我們現在可以談論生存功能𝑆(𝑡)或累積風險函數𝐻(𝑡),我們可以輕鬆地來回轉換。
下面兩個圖表表示上圖中風險函數和累積風險。
估計生存函數
這裏以Kaplan-Meier爲例:
Kaplan-Meier是一種非參數的估計方法。
簡單說一下非參數估計和參數估計
參數估計:我們通過一定的基本假設和建模獲得了待估計函數的形式,而有若干控制該函數具體表現的參數。而我們的目的是從形式已知參數未知的模型簇裏找出合適的參數。把這個函數當做我們對目標函數的估計。
非參數估計:我們並不對待估計的函數形式做任何假設,而是直接從數據出發去估計它。
優缺點:在數據量較小的時候,參數估計的表現會比較好。而非參數估計會由於數據量有限而無法表現出良好的性能。在數據量充足的時候,非參數估計由於減少了強加給數據的假設而會表現的比參數估計好。通常非參數估計的計算量大於參數估計。
單因素生存分析方法,可用生存率的估計、生存率比較及較影響因素分析。傾向於給與某種治療措施後生存時間的變化情況。大小樣本均適用,除比較因素外要求其他混雜因素組間均衡。當用分層變量控制混雜因素時,分層因素只限一個,且須是分類變量.
是在該時間點t實際死亡的人數,是在該時間點t有死亡風險的人數
數據演示
看下數據集
from lifelines.datasets import load_dd
data = load_dd()
data.info()
<class ‘pandas.core.frame.DataFrame’>
RangeIndex: 1808 entries, 0 to 1807
Data columns (total 12 columns):
Column | Non-Null | Count | Dtype |
---|---|---|---|
0 | ctryname | 1808 non-null | object |
1 | cowcode2 | 1808 non-null | int64 |
2 | politycode | 1801 non-null | float64 |
3 | un_region_name | 1808 non-null | object |
4 | un_continent_name | 1808 non-null | object |
5 | ehead | 1808 non-null | object |
6 | leaderspellreg | 1808 non-null | object |
7 | democracy | 1808 non-null | object |
8 | regime | 1808 non-null | object |
9 | start_year | 1808 non-null | int64 |
10 | duration | 1808 non-null | int64 |
11 | observed | 1808 non-null | int64 |
dtypes: float64(1), int64(4), object(7)
memory usage: 169.6+ KB
再lifelines包中,我們需要KaplanMeierFitter來進行訓練
from lifelines import KaplanMeierFitter
kmf = KaplanMeierFitter()
爲了進行此估算,我們需要每個領導人的上任時間/是否在職,以及是否被觀察到他們離開了辦公室(在2008年去世或就職的領導者,該數據記錄的最新日期,沒有觀察到死亡事件)
T = data["duration"]
E = data["observed"]
kmf.fit(T, event_observed=E,label='KM-estimate')
kmf.plot()
y軸表示領袖在t年後仍然在位的可能性,x軸爲t(年)
我們看到很少有領導者能夠任職20年以上。
kmf.median_survival_time_
4.0
中位數定義了平均50%的人口已經不在職的時間點
這意味着,在全球範圍內,當選領導人在四年或更短的時間內有50%的機會被停職!要獲得中位數的置信區間,可以使用:
from lifelines.utils import median_survival_times
median_ci = median_survival_times(kmf.confidence_interval_)
我們對民主政權與非民主政權進行細分
ax = plt.subplot(111)
dem = (data["democracy"] == "Democracy")
#民主政權
kmf.fit(T[dem], event_observed=E[dem], label="Democratic Regimes")
kmf.plot(ax=ax)
#非民主政權
kmf.fit(T[~dem], event_observed=E[~dem], label="Non-democratic Regimes")
kmf.plot(ax=ax)
plt.ylim(0, 1);
plt.title("Lifespans of different global regimes");
這些非民主政權的存在時間長了令人難以置信。民主政權確實對死亡有一種自然偏見:通過選舉和自然限制(美國規定了嚴格的八年限制)。非民主國家的中位數只有民主政權的兩倍左右,但差別卻顯而易見:如果您是非民主國家的領導人,並且已經超過10年大關,可能還有很長的路要走。同時,民主領袖很少能做到十年,而在那之後的壽命很短。
在這裏,生存功能之間的差異非常明顯,並且進行統計檢驗似乎很花哨。如果曲線更相似,或者我們擁有的數據更少,我們可能會對執行統計檢驗感興趣。在這種情況下,生命線包含lifelines.statistics用於比較兩個生存功能的例程 。下面我們演示這個例程。該功能lifelines.statistics.logrank_test()是生存分析中常用的統計測試,用於比較兩個事件序列的生成器。如果返回的值超過某個預先指定的值,則我們認爲該系列具有不同的生成器。
Nelson Aslen估計
累計風險函數–估計值的總和比逐點估計的穩定得多
是在時刻死亡的數量,是有潛在死亡的個人的數量
the number of susceptible individuals
T = data["duration"]
E = data["observed"]
from lifelines import NelsonAalenFitter
naf = NelsonAalenFitter()
naf.fit(T,event_observed=E)
print(naf.cumulative_hazard_.head())
naf.plot()
該曲線的變化率是風險函數的估計
naf.fit(T[dem], event_observed=E[dem], label="Democratic Regimes")
ax = naf.plot(loc=slice(0, 20))
naf.fit(T[~dem], event_observed=E[~dem], label="Non-democratic Regimes")
naf.plot(ax=ax, loc=slice(0, 20))
plt.title("Cumulative hazard function of different global regimes");
我們對民主政權與非民主政權進行細分
從變化的速度來看,儘管民主政權的持續風險性要高得多,但兩種政權都具有持續的風險性。
COX分析
定義
生存迴歸 -顧名思義,我們將協變量(例如年齡,國家/地區等)與另一個變量進行迴歸-在這種情況下爲持續時間。
生存迴歸中有一些流行的模型:Cox模型,加速故障模型和Aalen加法模型
在進行Cox迴歸分析前,如果樣本不多而變量較多,建議先通過單變量分析(KM法繪製生存曲線、Logrank檢驗等)考察所有自變量與因變量之間的關係,篩掉一些可能無意義的變量,再進行多因素分析,這樣可以保證結果更加可靠。即使樣本足夠大,也不建議把所有的變量放入方程直接分析,一定要先弄清楚各個變量之間的相互關係,確定自變量進入方程的形式,這樣纔能有效的進行分析。
單因素分析後,應當考慮應該將哪些自變量納入Cox迴歸模型。一般情況下,建議納入的變量有:1)單因素分析差異有統計學意義的變量(此時,最好將P值放寬一些,比如0.1或0.15等,避免漏掉一些重要因素);2)單因素分析時,沒有發現差異有統計學意義,但是臨牀上認爲與因變量關係密切的自變量。
Cox模型的基本假設爲
:
在任意一個時間點,兩組人羣發生時間的風險比例是恆定的;或者說其危險曲線應該是成比例而且是不能交叉的;也就是如果一個體在某個時間點的死亡風險是另外一個體的兩倍,那麼在其他任意時間點的死亡風險也同樣是2倍。
稱爲基準危險率。在上述產品中,部分危害是隨時間變化的標量因子,只會增加或減少基線危害。因此,協變量的變化只會增加或減少基線風險。
數據介紹
用一個具體的數據演示一下
生存迴歸所需的數據集必須採用Pandas DataFrame格式。DataFrame的每一行都應該是一個觀察值。應該有一欄表示觀察的持續時間。可能會有一欄表示每個觀察的事件狀態(如果發生事件,則爲1,如果截斷,則爲0)。還希望對其他協變量進行迴歸。
使用的示例數據集是Rossi累犯數據集
from lifelines.datasets import load_rossi
rossi = load_rossi()
"""
week arrest fin age race wexp mar paro prio
0 20 1 0 27 1 0 0 1 3
1 17 1 0 18 1 0 0 1 8
2 25 1 0 19 0 1 0 1 13
3 52 0 1 23 1 1 1 1 1
"""
DataFrame rossi包含432個觀察值。該week列是持續時間,arrest列事件發生,而另一列代表我們要對迴歸變量。
統計分析
from lifelines import CoxPHFitter
from lifelines.datasets import load_rossi
rossi_dataset = load_rossi()
cph = CoxPHFitter()
cph.fit(rossi_dataset, duration_col='week', event_col='arrest')
cph.print_summary()
z值代表Wald統計量,其值等於迴歸係數coef除以其標準誤se(coef),即z = coef/se(coef);有統計量必有其對應的假設檢驗的顯著性P值,其說明coef是否與0有統計學意義上的顯著差別。
例子
prio(先前的逮捕次數)係數約爲0.09。因此,一個單位的增加prio意味着基準危害將增加一個係數。exp(0.09)=1.10大約增加10%
。回想一下,在Cox比例風險模型中,較高的風險意味着事件發生的風險更大。價值exp(0.09)稱爲危險比,這個名稱在另一個示例中將很明顯。
考慮係數mar(對象是否已婚)。列中的值爲二進制:0或1,表示未婚或已婚。與mar,相關的係數值exp(−.43),是與結婚相關的危險比的值,即:
請注意,左側是一個常數(具體來說,它與時間無關, 𝑡),但右側有兩個因素可能會隨時間而變化。該比例風險的假設是這種關係是真實的。也就是說,危險可以隨時間變化,但是它們之間的比率保持不變。稍後我們將處理檢查此假設。但是,實際上,危險比在研究期間會發生變化是非常普遍的。然後,危險比可以解釋特定時期的危險比的某種加權平均值。結果,危險比可能嚴重取決於隨訪的持續時間。
係數繪製
cph.plot()
繪製改變協變量的效果
cph.plot_covariate_groups('prio', [0, 2, 4, 6, 8, 10], cmap='coolwarm')
github
參考:
https://wenku.baidu.com/view/13b07ba53169a4517623a378.html
https://zhuanlan.zhihu.com/p/34802509
https://lifelines.readthedocs.io/en/latest/Survival%20analysis%20with%20lifelines.html
https://segmentfault.com/a/1190000015895321
https://www.jianshu.com/p/e80eb4168043