组内时间到事件计数器

时间:2017-06-20 20:17:35

标签: python pandas

我正在努力研究流失预测的方法,我发现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_censoredtime_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会在我睡觉的时候怜悯我(在欧盟工作),明天我会再接受这个问题。将更新我发现的任何东西。非常感谢提前!

2 个答案:

答案 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或等于它。