假设我有一个熊猫数据框,其中有两列:ID和Days。 DataFrame在两个变量中均按升序排序。例如:
# Initial dataset
data = pd.DataFrame({'id': np.repeat([1, 2 ,3], 4),
'day': [1, 2, 10, 11, 3, 4, 12, 15, 1, 20, 21, 24]})
id day
0 1 1
1 1 2
2 1 10
3 1 11
4 2 3
5 2 4
6 2 12
7 2 15
8 3 1
9 3 20
10 3 21
11 3 24
我想添加第三列,该列将为每个ID *天提供一个“会话”编号。 “会话”是指一系列天,一次会话的天数之差少于2天。例如,序列5,6,7
将被视为一个会话,而5,6,9
将被视为两个会话,应将其标记为0, 0, 1
,即第5天和第6天被称为会话#0 ,而第9天则参考了会话1。
每个新ID的会话号都应从0
开始。
换句话说,我想得到的是:
id day session
0 1 1 0
1 1 2 0
2 1 10 1
3 1 11 1
4 2 3 0
5 2 4 0
6 2 12 1
7 2 15 2
8 3 1 0
9 3 20 1
10 3 21 1
11 3 24 2
要解决此任务,我使用基本的for循环。在此循环中,我反复遍历所有唯一ID,然后从初始数据集中子集一个数据块,并为特定ID的每一天分配会话号。我遇到的问题-由于初始数据集是数百万行-循环需要大量时间!例如,对于一百万行,我的循环花了大约一分钟,这太多了。
如何提高速度?任何方法都是好的!例如,如果您知道如何获得期望的结果,可以使用一些numpy矩阵操作来减少时间-也是很好的...
我的循环代码:
# Get sessions for every id
sessions = []
for i in data.id.unique():
id_data = data['day'][data['id']==i].reset_index(drop=True)
for ind in id_data.index:
if ind == 0:
temp = [0]
elif ((id_data[ind] - id_data[ind - 1]) < 2):
temp.append(temp[ind - 1])
else:
temp.append(temp[ind - 1] + 1)
sessions.extend(temp)
# Add sessions to the table
data['session'] = sessions
答案 0 :(得分:8)
您可以对布尔值进行求和
data.groupby('id').day.apply(lambda x : x.diff().gt(1).cumsum())
Out[614]:
0 0
1 0
2 1
3 1
4 0
5 0
6 1
7 2
8 0
9 1
10 1
11 2
Name: day, dtype: int32
答案 1 :(得分:5)
我们可以利用您的数据经过排序的事实,以消除fillna
,减少两个groupby
调用到一个,并消除对apply
的需求。
df['session'] = df.day.diff().ge(2)
df['session'] = df.groupby('id').session.cumsum()
df
id day session
0 1 1 0.0
1 1 2 0.0
2 1 10 1.0
3 1 11 1.0
4 2 3 0.0
5 2 4 0.0
6 2 12 1.0
7 2 15 2.0
8 3 1 0.0
9 3 20 1.0
10 3 21 1.0
11 3 24 2.0
作为回报,"session"
将是一个浮点列。
答案 2 :(得分:4)
您可以将groupby()
与np.where()
,diff()
和cumsum()
一起使用两次:
data['session'] = np.where(data.groupby('id')['day'].diff().fillna(0)>1, 1, 0)
data['session'] = data.groupby('id')['session'].cumsum()
收益:
id day session
0 1 1 0
1 1 2 0
2 1 10 1
3 1 11 1
4 2 3 0
5 2 4 0
6 2 12 1
7 2 15 2
8 3 1 0
9 3 20 1
10 3 21 1
11 3 24 2