我有id发生的事件表。我如何计算在当前行之前每个事件类型发生的最后n天的次数?
例如,包含以下事件列表:
df = pd.DataFrame([{'id': 1, 'event_day': '2016-01-01', 'event_type': 'type1'},
{'id': 1, 'event_day': '2016-01-02', 'event_type': 'type1'},
{'id': 2, 'event_day': '2016-02-01', 'event_type': 'type2'},
{'id': 2, 'event_day': '2016-02-15', 'event_type': 'type3'},
{'id': 3, 'event_day': '2016-01-06', 'event_type': 'type3'},
{'id': 3, 'event_day': '2016-03-11', 'event_type': 'type3'},])
df['event_day'] = pd.to_datetime(df['event_day'])
df = df.sort_values(['id', 'event_day'])
或:
event_day event_type id
0 2016-01-01 type1 1
1 2016-01-02 type1 1
2 2016-02-01 type2 2
3 2016-02-15 type3 2
4 2016-01-06 type3 3
5 2016-03-11 type3 3
id
我想计算在过去n天内当前行之前每个event_type
发生的次数。例如,在第3行id = 2中,那么在事件历史记录中该点的次数(但不包括)有多少次事件类型1,2和3发生在id 2的最后n天?
所需的输出如下所示:
event_day event_type event_type1_in_last_30days event_type2_in_last_30days event_type3_in_last_30days id
0 2016-01-01 type1 0 0 0 1
1 2016-01-02 type1 1 0 0 1
2 2016-02-01 type2 0 0 0 2
3 2016-02-15 type3 0 1 0 2
4 2016-01-06 type3 0 0 0 3
5 2016-03-11 type3 0 0 0 3
答案 0 :(得分:2)
res = ((((df['event_day'].values >= df['event_day'].values[:, None] - pd.to_timedelta('30 days'))
& (df['event_day'].values < df['event_day'].values[:, None]))
& (df['id'].values == df['id'].values[:, None]))
.dot(pd.get_dummies(df['event_type'])))
res
Out:
array([[ 0., 0., 0.],
[ 1., 0., 0.],
[ 0., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
第一部分是按如下方式生成矩阵:
(df['event_day'].values >= df['event_day'].values[:, None] - pd.to_timedelta('30 days'))
Out:
array([[ True, True, True, True, True, True],
[ True, True, True, True, True, True],
[False, True, True, True, True, True],
[False, False, True, True, False, True],
[ True, True, True, True, True, True],
[False, False, False, True, False, True]], dtype=bool)
它是一个6x6矩阵,每行都与其他行进行比较。它利用NumPy的广播进行成对比较(.values[:, None]
添加另一个轴)。为了完成它,我们需要检查这一行是否比另一行更早出现:
(((df['event_day'].values >= df['event_day'].values[:, None] - pd.to_timedelta('30 days'))
& (df['event_day'].values < df['event_day'].values[:, None])))
Out:
array([[False, False, False, False, False, False],
[ True, False, False, False, False, False],
[False, True, False, False, True, False],
[False, False, True, False, False, False],
[ True, True, False, False, False, False],
[False, False, False, True, False, False]], dtype=bool)
另一个条件是关于id的。使用类似的方法,您可以构建一个成对比较矩阵,显示id匹配时:
(df['id'].values == df['id'].values[:, None])
Out:
array([[ True, True, False, False, False, False],
[ True, True, False, False, False, False],
[False, False, True, True, False, False],
[False, False, True, True, False, False],
[False, False, False, False, True, True],
[False, False, False, False, True, True]], dtype=bool)
变成:
(((df['event_day'].values >= df['event_day'].values[:, None] - pd.to_timedelta('30 days'))
& (df['event_day'].values < df['event_day'].values[:, None]))
& (df['id'].values == df['id'].values[:, None]))
Out:
array([[False, False, False, False, False, False],
[ True, False, False, False, False, False],
[False, False, False, False, False, False],
[False, False, True, False, False, False],
[False, False, False, False, False, False],
[False, False, False, False, False, False]], dtype=bool)
最后,您希望为每种类型看到它,以便您可以使用get_dummies:
pd.get_dummies(df['event_type'])
Out:
type1 type2 type3
0 1.0 0.0 0.0
1 1.0 0.0 0.0
2 0.0 1.0 0.0
3 0.0 0.0 1.0
4 0.0 0.0 1.0
5 0.0 0.0 1.0
如果将得到的矩阵与此矩阵相乘,它应该为您提供满足每种类型的条件的行数。您可以将结果数组传递给DataFrame构造函数并连接:
pd.concat([df, pd.DataFrame(res, columns = ['e1', 'e2', 'e3'])], axis=1)
Out:
event_day event_type id e1 e2 e3
0 2016-01-01 type1 1 0.0 0.0 0.0
1 2016-01-02 type1 1 1.0 0.0 0.0
2 2016-02-01 type2 2 0.0 0.0 0.0
3 2016-02-15 type3 2 0.0 1.0 0.0
4 2016-01-06 type3 3 0.0 0.0 0.0
5 2016-03-11 type3 3 0.0 0.0 0.0
答案 1 :(得分:2)
好的,我真的很喜欢ayhan的方法。但我有另一个可能更慢(只是我假设apply
通常很慢),虽然我认为逻辑更直接。如果有人想要比较两者,特别是它们如何扩展,我会非常感兴趣:
In [1]: import pandas as pd, numpy as np
In [2]: df = pd.DataFrame([{'id': 1, 'event_day': '2016-01-01', 'event_type': 'type1'},
{'id': 1, 'event_day': '2016-01-02', 'event_type': 'type1'},
{'id': 2, 'event_day': '2016-02-01', 'event_type': 'type2'},
{'id': 2, 'event_day': '2016-02-15', 'event_type': 'type3'},
{'id': 3, 'event_day': '2016-01-06', 'event_type': 'type3'},
{'id': 3, 'event_day': '2016-03-11', 'event_type': 'type3'},])
In [3]: df['event_day'] = pd.to_datetime(df['event_day'])
In [4]: df = df.sort_values(['id', 'event_day'])
In [5]: dummies = pd.get_dummies(df)
In [6]: dummies.set_index('event_day', inplace=True)
In [7]: dummies
Out[7]:
id event_type_type1 event_type_type2 event_type_type3
event_day
2016-01-01 1 1.0 0.0 0.0
2016-01-02 1 1.0 0.0 0.0
2016-02-01 2 0.0 1.0 0.0
2016-02-15 2 0.0 0.0 1.0
2016-01-06 3 0.0 0.0 1.0
2016-03-11 3 0.0 0.0 1.0
In [8]: import datetime
In [9]: delta30 = datetime.timedelta(days=30)
In [10]: delta1 = datetime.timedelta(days=1)
In [11]: dummies.apply(lambda x: dummies[dummies.id == x.id].loc[x.name - delta30:x.name - delta1].sum() ,axis=1)
Out[11]:
id event_type_type1 event_type_type2 event_type_type3
event_day
2016-01-01 0.0 0.0 0.0 0.0
2016-01-02 1.0 1.0 0.0 0.0
2016-02-01 0.0 0.0 0.0 0.0
2016-02-15 2.0 0.0 1.0 0.0
2016-01-06 0.0 0.0 0.0 0.0
2016-03-11 0.0 0.0 0.0 0.0
最后,您可以在删除&#39; id&#39;之后merge
dummies
和原始数据框。 dummies
中的列:
In [12]: dummies.drop('id', inplace = True,axis=1)
In [13]: dummies
Out[13]:
event_day event_type_type1 event_type_type2 event_type_type3
0 2016-01-01 0.0 0.0 0.0
1 2016-01-02 1.0 0.0 0.0
2 2016-02-01 0.0 0.0 0.0
3 2016-02-15 0.0 1.0 0.0
4 2016-01-06 0.0 0.0 0.0
5 2016-03-11 0.0 0.0 0.0
In [14]: pd.merge(df, dummies, on="event_day")
Out[14]:
event_day event_type id event_type_type1 event_type_type2 \
0 2016-01-01 type1 1 0.0 0.0
1 2016-01-02 type1 1 1.0 0.0
2 2016-02-01 type2 2 0.0 0.0
3 2016-02-15 type3 2 0.0 1.0
4 2016-01-06 type3 3 0.0 0.0
5 2016-03-11 type3 3 0.0 0.0
event_type_type3
0 0.0
1 0.0
2 0.0
3 0.0
4 0.0
5 0.0