使用先前的行引用快速遍历Python数据框

时间:2018-10-01 21:20:08

标签: python pandas loops

假设我有一个熊猫数据框,其中有两列: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 

3 个答案:

答案 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