生存分析(Survival Analysis)來源於基礎醫學領域,最先用來研究各類治療方案對病人壽命的影響。而壽命則用一個end event(死亡)的方式衡量。基本定義以下:python
T爲標記事件發生的時間。git
$S(t) = P(T > t)$github
$\lambda (t) = {lim}_{h\to 0} \frac{P(t \le T < t + h | T \ge t)}{h}$函數
$S(t) = e^{-\int_{0}^t \lambda (s)ds}$ui
然而,生存分析的定義自己讓它和不少用戶行爲事件自己發生聯繫。譬如,用戶流失和生存分析研究的概念徹底一致,而常見的流失分析,可能會存在如下問題:spa
而生存分析自己就會對以上三個內容作出預測,此外,對Customer Lifetime Value能提出一些更有價值的洞識。3d
但本文試圖擴展生存分析的試用範圍,任何具備觸發時間特徵的事件,均可以採用生存分析的方式作出分析,譬如:code
本文以常見的購買行爲的轉化爲例,介紹生存分析的某些應用。blog
代碼以
jupyter notebook
的形式放在
github這裏。
本實例是研究,用戶從註冊時開始,隨時間變化其轉化率會有何種變化,考慮屆時提供一些運營策略。常規計算本任務轉化率的方式是:three
轉化率 = # 轉化用戶數 / # 總用戶數數
這個轉化率每每很難提供更多的洞識,咱們沒法給出自動化運營能夠介入的時間要素。而生存分析從本質上說,偏偏是研究一個發生事件的機率隨時間發生的變化,在此項任務中,事件顯然就是轉化。基於這種思想,咱們作出以下分析。
生存分析須要以一個用戶爲單位,提取參與實驗的時間(這裏就是註冊時間),終止實驗/end event發生時間(這裏就是指數據採集的截止日期和用戶發生轉化的時間),以及最後是否發生end event的標記。
所以咱們採集了以下數據。其中sex
,birth_year
,province
三個字段用之後面的分析,duration
則是到事件發生時總共花費的時間,即test_last_time - signup_time
。
user_id | signup_time | end_time | test_last_time | buy_time | sex | birth_year | province | have_bought | duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 513336 | 2017-10-30 21:58:04.323000 | 2017-10-30 22:03:54.430000 | 2018-06-01 00:00:00 | 2017-10-30 22:03:54.430000 | 女 | 1991 | 山東 | True | 0.00405216 |
1 | 513340 | 2017-10-30 22:14:35.853000 | 2017-10-30 22:45:10.243000 | 2018-06-01 00:00:00 | 2017-10-30 22:45:10.243000 | 男 | 1988 | 安徽 | True | 0.0212314 |
2 | 513353 | 2017-10-31 07:29:04.293000 | 2018-06-01 00:00:00 | 2018-06-01 00:00:00 | NaT | nan | nan | nan | False | 212.688 |
3 | 513370 | 2017-10-31 10:45:45.586000 | 2018-06-01 00:00:00 | 2018-06-01 00:00:00 | NaT | 女 | 1979 | 河南 | False | 212.552 |
4 | 513387 | 2017-10-31 13:00:32.360000 | 2017-10-31 13:09:38.100000 | 2018-06-01 00:00:00 | 2017-10-31 13:09:38.100000 | 女 | 1992 | 山東 | True | 0.00631644 |
Python
比較完整的支持生存分析的包是Liflines
和scikit-survival
。二者之間,對分析友好的方案是前者,咱們在本博文中,也主要採用該模塊進行分析。
from lifelines import NelsonAalenFitter, CoxPHFitter, KaplanMeierFitter from lifelines.statistics import logrank_test
首先,咱們須要查看用戶的未轉化率(有點繞口),這直接能夠用生存分析自己的定義來實現。
kmf = KaplanMeierFitter() kmf.fit(df['duration'], event_observed=df['have_bought'], label='all') kmf.plot()
image-20180805225520337.png
顯然,咱們能夠發現,在50天后,基本上變化不明顯了,最初的50天內,用40%多的用戶發生轉化。固然,這一圖示並不直觀,咱們能夠用1 - 未轉化率=轉化率
的方式來從新繪製轉化率曲線。
(1 - kmf.survival_function_).plot()
image-20180805230116015.png
顯然,這張圖已經能夠參與到運營決策中取了,圖中顯示,用戶會在前10天內轉化,後期變化不在明顯。但咱們能夠經過生存分析獲得更多有意思的結論。
一個例子就是分析不一樣產品的生存曲線圖來分析產品之間的好壞(在此不作示範),一個就是用戶自己特徵對轉化的影響。在此,咱們以性別爲例,分析性別對用戶更早決定購買產品/轉化之間有何有何關係。
ax = subplot(111) t = np.linspace(0, 50, 51) kmf.fit(df[df['sex'] == '男']['duration'], event_observed=df[df['sex'] == '男']['have_bought'], timeline=t, label="male") ax = kmf.plot(ax=ax) kmf.fit(df[df['sex'] == '女']['duration'], event_observed=df[df['sex'] == '女']['have_bought'], timeline=t, label="female") ax = kmf.plot(ax=ax) plt.ylim(0,1) plt.title("not buying rate between two gender");
image-20180805230614522.png
ax = subplot(111) t = np.linspace(0, 50, 51) kmf.fit(df[df['sex'] == '男']['duration'], event_observed=df[df['sex'] == '男']['have_bought'], timeline=t, label="male") ax = (1 - kmf.survival_function_).plot(ax=ax) kmf.fit(df[df['sex'] == '女']['duration'], event_observed=df[df['sex'] == '女']['have_bought'], timeline=t, label="female") ax = (1 - kmf.survival_function_).plot(ax=ax) plt.ylim(0,1) plt.title("conversion rate between two gender");
image-20180805230718723.png
顯然,咱們從圖中能夠發現,女性更傾向於更快相信此產品,更早時間而且更多比例的發現轉化。接下來,咱們能夠利用自帶的log-rank test
對二者是否差別顯著作預測。
logrank_test(event_times_A=df[df['sex']=='男']['duration'], event_observed_A=df[df['sex']=='男']['duration'], event_times_B=df[df['sex']=='女']['duration'], event_observed_B=df[df['sex']=='女']['duration'])
<lifelines.StatisticalResult: t_0=-1, alpha=0.95, null_distribution=chi squared, df=1 test_statistic p 82.2898 0.0000 *** --- Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 >
顯然,差別是顯著的。
在此,咱們使用Nelson Aslen
方法分析轉化率的變化率隨時間的變化(即Hazard函數)。
naf = NelsonAalenFitter() naf.fit(df['duration'], event_observed=df['have_bought'], timeline=t, label='all') naf.plot_hazard(bandwidth=20)
image-20180805231451494.png
圖中能夠看出,轉化率的變化在前5天內略微提升,隨後斷崖式減小,大概在20天左右區域和緩。換用運營的術語,前五天內用戶對產品的信任度略微增長,使得更有可能購買產品;但超過五天尚未購買意願的人,購買發生的可能性隨時間遞減。
一個可能的運營策略就是在第5天作出一些行爲,增長用戶的購買意願,從而達到提升用戶的生命週期的總價值。
固然,咱們如同上面板塊同樣,也不叫一下性別差別。
image-20180805231922162.png
顯然,女性創建信任的速度更快,可是大體都是以5天爲界,在5天時沒有發生購買行爲,後期發生的機率會愈來愈小。
在此,咱們還介紹一種對於不一樣變量對生存曲線影響的分析方法,並能夠做爲預測用戶是否可能發生轉化的模型——Cox PH
模型。其基本假設是:
$\lambda (t, X) = \lambda_0(t) exp(\beta X)$
即假設待研究的變量不影響到生存模型的形狀,而形狀只有獨立的$$\lambda_0(t)$$決定(即只和時間有關)。對此,咱們須要對變量進行形狀上的驗證來判斷該模型的有效性。在此咱們只作性別和用戶年齡的迴歸分析。
上面已經驗證過性別的轉化率的形狀,咱們使用雙對數繪製對年齡(出生年)的影響。
kmf0 = KaplanMeierFitter() kmf0.fit(cph_train_df[cph_train_df['birth_year'] == 1960]['duration'], event_observed=cph_train_df[cph_train_df['birth_year'] == 1960]['have_bought']) kmf1 = KaplanMeierFitter() kmf1.fit(cph_train_df[cph_train_df['birth_year'] == 1970]['duration'], event_observed=cph_train_df[cph_train_df['birth_year'] == 1970]['have_bought']) fig, axes = plt.subplots() kmf0.plot_loglogs(ax=axes) kmf1.plot_loglogs(ax=axes) axes.legend(['1960', '1970']) plt.show()
image-20180805233353267.png
結果不是很符合預設,但因爲演示須要,假設數據符合咱們的假定,並且咱們僅作線性模型。
cph = CoxPHFitter() cph.fit(cph_train_df, duration_col='duration', event_col='have_bought', show_progress=True) cph.print_summary()
n=10000, number of events=7683 coef exp(coef) se(coef) z p lower 0.95 upper 0.95 birth_year -0.0084 0.9916 0.0010 -8.7945 0.0000 -0.0103 -0.0066 *** sex -0.1676 0.8457 0.0241 -6.9492 0.0000 -0.2149 -0.1203 *** --- Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 Concordance = 0.528 Likelihood ratio test = 124.891 on 2 df, p=0.00000
咱們能夠發現,sex影響的項更大,參數都是顯著的。以後,咱們就能夠用cph
的相關函數對數據進行預測。咱們在此用plot_covariate_group
函數來展現不一樣屬性的影響:
cph.plot_covariate_groups('birth_year', [1960, 1970, 1980, 1990]) cph.plot_covariate_groups('sex', [0, 1])
image-20180805234322978.png
image-20180805234340974.png
顯然,年齡越大、性別爲女性越容易信任平臺,更早的作出購買行爲。
這裏,咱們用生存分析解決並使用在用戶行爲分析中。給出更多可能有助於運營以及理解用戶畫像的洞識結論。