我正在努力研究流失预测的方法,我发现here:
让我们说今天是1/6/2017。我有一个pandas数据帧,df,我想添加两列。
df = pd.DataFrame([
['a', '2017-01-01', 0],
['a', '2017-01-02', 0],
['a', '2017-01-03', 0],
['a', '2017-01-04', 1],
['a', '2017-01-05', 1],
['b', '2017-01-01', 0],
['b', '2017-01-02', 1],
['b', '2017-01-03', 0],
['b', '2017-01-04', 0],
['b', '2017-01-05', 0]
]
,columns=['id','date','is_event']
)
df['date'] = pd.to_datetime(df['date'])
一个是time_to_next_event
,另一个是is_censored
。 time_to_next_event
会在每个内容中随着事件越来越接近而减少到零。如果今天之前没有任何事件,time_to_next_event
将减少值,直到该组结束。
is_censored
是与此现象相关的二进制标志,将在每个id中指示最近事件与今天之间发生的行。对于id a,最近一行包含事件,因此整个组is_censored
为零。对于id b,最近的事件和今天之间有三行,因此每个is_censored
值都是1.
desired = pd.DataFrame([
['a', '2017-01-01', 0, 3, 0],
['a', '2017-01-02', 0, 2, 0],
['a', '2017-01-03', 0, 1, 0],
['a', '2017-01-04', 1, 0, 0],
['a', '2017-01-05', 1, 0, 0],
['b', '2017-01-01', 0, 1, 0],
['b', '2017-01-02', 1, 0, 0],
['b', '2017-01-03', 0, 3, 1],
['b', '2017-01-04', 0, 2, 1],
['b', '2017-01-05', 0, 1, 1]
]
,columns=['id','date','is_event','time_to_next_event', 'is_censored']
)
desired['date'] = pd.to_datetime(desired['date'])
对于time_to_next_event,我发现this SO question但是很难让它适合我的用例。
对于is_censored,到目前为止我很难过。我正在发布这个问题,希望一些仁慈的Stack Overflower会在我睡觉的时候怜悯我(在欧盟工作),明天我会再接受这个问题。将更新我发现的任何东西。非常感谢提前!
答案 0 :(得分:2)
要了解下一个事件的日期,我们可以添加一个回填下一个事件日期的列:
df['next_event'] = df['date'][df['is_event'] == 1]
df['next_event'] = df.groupby('id')['next_event'].transform(lambda x: x.fillna(method='bfill'))
然后我们可以减去以获得下一个事件和每一天之间的日期:
df['next_event'] = df['next_event'].fillna(df['date'].iloc[-1] + pd.Timedelta(days=1))
df['time_to_next_event'] = (df['next_event']-df['date']).dt.days
要获取每天和每个id的is_censored值,我们可以按ID分组,然后我们可以根据每个组的“is_event”列进行前向填充。现在,我们只需要前向填充值,因为根据上面的定义,'is_censored'的值应该在事件本身的当天为0。因此,我们可以将'is_event'列与该列的前向填充版本进行比较,并在每次我们有一个不在原始值中的前向填充值时将'is_censored'设置为1。
df['is_censored'] = (df.groupby('id')['is_event'].transform(lambda x: x.replace(0, method='ffill')) != df['is_event']).astype(int)
df = df.drop('next_event', axis=1)
In [343]: df
Out[343]:
id date is_event time_to_next_event is_censored
0 a 2017-01-01 0 3 0
1 a 2017-01-02 0 2 0
2 a 2017-01-03 0 1 0
3 a 2017-01-04 1 0 0
4 a 2017-01-05 1 0 0
5 b 2017-01-01 0 1 0
6 b 2017-01-02 1 0 0
7 b 2017-01-03 0 3 1
8 b 2017-01-04 0 2 1
9 b 2017-01-05 0 1 1
答案 1 :(得分:0)
为了概括is_censored
的方法,以包含每个id
内事件多次发生的情况,我写道:
df['is_censored2'] = 1
max_dates = df[df['is_event'] == 1].groupby('id',as_index=False)['date'].max()
max_dates.columns = ['id','max_date']
df = pd.merge(df,max_dates,on=['id'],how='left')
df['is_censored2'][df['date'] <= df['max_date']] = 0
它将列初始化为1,然后获取与每个id
内的事件相关联的最大日期,如果is_censored2
中的任何日期小于id
,则填充csv
中的0或等于它。