用最后一组的最后一个值填充组列

时间:2018-08-25 06:08:02

标签: python pandas performance

这是我要提高代码性能的棘手问题。想象这样一个数据框:

TOUR_ID  ID    PAGE_ID     CREATED DATE         AVAILABILITY    

T_1      ID1      P1      2018-07-03 19:10:19     AVAILABLE     
T_1      ID1      P1      2018-07-03 19:10:20     AVAILABLE     
T_1      ID1      P2      2018-07-03 19:12:33     AVAILABLE     
T_1      ID2      P3      2018-07-03 19:13:34     AVAILABLE 
T_1      ID2      P3      2018-07-03 19:13:35     NOT AVAILABLE     
T_1      ID2      P4      2018-07-03 19:16:24     AVAILABLE     

T_2      ID3      P4      2018-07-03 18:23:19     AVAILABLE       
T_2      ID3      P4      2018-07-03 18:23:20     NOT AVAILABLE   
T_2      ID1      P1      2018-07-03 19:10:21     NOT AVAILABLE     
T_2      ID2      P3      2018-07-03 19:13:37     NOT AVAILABLE 
T_2      ID2      P3      2018-07-03 19:13:38     NOT AVAILABLE     
T_2      ID3      P5      2018-07-03 20:56:33     AVAILABLE       
T_2      ID3      P5      2018-07-03 20:56:34     NOT AVAILABLE   
T_2      ID3      P5      2018-07-03 22:56:35     AVAILABLE       
T_2      ID3      P6      2018-07-03 22:57:20     NOT AVAILABLE   
T_2      ID3      P7      2018-07-03 22:58:35     AVAILABLE       
T_2      ID4      P8      2018-07-03 22:59:00     AVAILABLE     
T_2      ID1      P1      2018-07-03 23:12:00     AVAILABLE     
T_2      ID1      P3      2018-07-03 23:32:00     AVAILABLE         

在每个组(Tour_ID,ID,Page_ID)上,我需要使用上一个组的最后一个值创建一列。另外,在第一次对tour_ID或ID进行更改时,我将获得NaN,因为该组合没有任何先前的值。

结果应如下所示:

TOUR_ID   ID    PAGE_ID     CREATED DATE         AVAILABILITY   PREVIOUS AVAILABILITY    

T_1      ID1      P1      2018-07-03 19:10:19     AVAILABLE            NaN     
T_1      ID1      P1      2018-07-03 19:10:20     AVAILABLE            NaN
T_1      ID1      P2      2018-07-03 19:12:33     AVAILABLE         AVAILABLE
T_1      ID2      P3      2018-07-03 19:13:34     AVAILABLE            NaN
T_1      ID2      P3      2018-07-03 19:13:35     NOT_AVAILABLE        NaN
T_1      ID2      P4      2018-07-03 19:16:24     AVAILABLE       NOT_AVAILABLE       

T_2      ID3      P4      2018-07-03 18:23:19     AVAILABLE            NaN
T_2      ID3      P4      2018-07-03 18:23:20     NOT AVAILABLE        NaN
T_2      ID1      P1      2018-07-03 19:10:21     NOT AVAILABLE        NaN
T_2      ID2      P3      2018-07-03 19:13:37     NOT AVAILABLE        NaN
T_2      ID2      P3      2018-07-03 19:13:38     NOT AVAILABLE        NaN
T_2      ID3      P5      2018-07-03 20:56:33     AVAILABLE       NOT AVAILABLE
T_2      ID3      P5      2018-07-03 20:56:34     NOT AVAILABLE   NOT AVAILABLE
T_2      ID3      P5      2018-07-03 22:56:35     AVAILABLE       NOT AVAILABLE
T_2      ID3      P6      2018-07-03 22:57:20     NOT AVAILABLE     AVAILABLE
T_2      ID3      P7      2018-07-03 22:58:35     AVAILABLE       NOT AVAILABLE
T_2      ID4      P8      2018-07-03 22:59:00     AVAILABLE            NaN
T_2      ID1      P1      2018-07-03 23:12:00     AVAILABLE            NaN
T_2      ID1      P3      2018-07-03 23:32:00     AVAILABLE         AVAILABLE

我有一个可以运行的代码,但是它伸缩性不好(数据帧大约有900,000行)。对于改善代码性能的任何帮助,将不胜感激。

这是我到目前为止所拥有的:

for current_op in df.TOUR_ID.unique():    
    dummy = df[df.TOUR_ID == current_op].ID.unique()

    for current_ID in dummy:
        dummy_m = df[(df.TOUR_ID == current_op) & (df.ID == current_ID)].PAGE_ID.unique()

        for current_page in dummy_m:
            mask = (df.TOUR_ID == current_op) & (df.ID == current_ID) & (df.PAGE_ID == current_page)
            indexes = mask.reset_index().rename(columns ={0:'Bool'})
            ind = indexes.index[indexes['Bool'] == True].tolist()[0]

            if (ind == 0) | ((current_page == dummy_m[0])):
                df.loc[mask,'Previous_availability'] = np.nan
            else:
                previous_aval = df.AVAILABILITY.loc[indexes['index'].loc[ind-1]]

                df.loc[mask, 'Previous_availability'] = previous_aval

注意:NaN最终将被丢弃

-编辑

下面是创建数据框的代码:

 import pandas as pd 
 import numpy as np
 df = pd.DataFrame([['T_1','ID1','P1','2018-07-03 19:10:19', 'AVAILABLE'],
               ['T_1','ID1','P1','2018-07-03 19:10:20', 'AVAILABLE'],
               ['T_1','ID1','P2','2018-07-03 19:12:33', 'AVAILABLE'],

               ['T_1','ID2','P3','2018-07-03 19:13:34', 'AVAILABLE'],
               ['T_1','ID2','P3','2018-07-03 19:13:35', 'NOT AVAILABLE'],
               ['T_1','ID2','P4','2018-07-03 19:16:24', 'AVAILABLE'],

               ['T_2','ID3','P4','2018-07-03 18:23:19', 'AVAILABLE'],
               ['T_2','ID3','P4','2018-07-03 18:23:20', 'NOT AVAILABLE'],
               ['T_2','ID1','P1','2018-07-03 19:10:21', 'NOT AVAILABLE'],
               ['T_2','ID2','P3','2018-07-03 19:13:36', 'NOT AVAILABLE'],
               ['T_2','ID2','P3','2018-07-03 19:13:37', 'NOT AVAILABLE'],
               ['T_2','ID3','P5','2018-07-03 20:56:33', 'AVAILABLE'],
               ['T_2','ID3','P5','2018-07-03 20:56:34', 'NOT AVAILABLE'],
               ['T_2','ID3','P5','2018-07-03 22:56:35', 'AVAILABLE'],
               ['T_2','ID3','P6','2018-07-03 22:57:20', 'NOT AVAILABLE'],
               ['T_2','ID3','P7','2018-07-03 22:58:35', 'AVAILABLE'],
               ['T_2','ID4','P8','2018-07-03 22:59:00', 'AVAILABLE'],
               ['T_2','ID1','P1','2018-07-03 23:12:00', 'AVAILABLE'],
               ['T_2','ID1','P3','2018-07-03 23:32:00', 'AVAILABLE']

              ], columns=['TOUR_ID','ID','PAGE_ID','CREATED DATE', 'AVAILABILITY'])

2 个答案:

答案 0 :(得分:1)

那真是个大难题,但这是解决这个问题的一种方法:

df = pd.read_csv('test.tsv').set_index(['TOUR_ID', 'ID', 'PAGE_ID'])

获取每组的最后一行,向前移动一位:

shifted = df.groupby(['TOUR_ID', 'ID', 'PAGE_ID']).last().shift(1).reset_index()

现在,我们对在PAGE_ID中看到变化但在ID中看不到变化的行感兴趣,因此我们构造了一个布尔掩码:

change = shifted != shifted.shift(1)
mask = np.array(change.PAGE_ID & ~change.ID & ~change.TOUR_ID)

最后,我们应用遮罩并加入以创建新列:

shifted.set_index(['TOUR_ID', 'ID', 'PAGE_ID'], inplace=True)

shifted[~mask] = np.nan

result = df.join(shifted['AVAILABILITY'], rsuffix='LAST')

答案 1 :(得分:0)

好,这是我的刺路。

1)创建助手系列P_INTPAGE_ID的整数部分)

2)使用MultiIndex df_last_availability创建助手DataFrame ['TOUR_ID', 'ID', 'P_INT']

3)将P_INT偏移1

4)重置原始df的索引,使其与df_last_availability相匹配。从这里,您可以轻松地(使用左联接)合并索引上的2个数据框。

5)最后一种链接方法只是清除操作,以将数据帧恢复为其原始形状-即删除辅助字段并将索引重置为原始值。

df['P_INT'] = df.PAGE_ID.str.extract('(\d+)').astype(int)
df_last_availability = df.groupby(['TOUR_ID', 'ID', 'P_INT']).last()
df['P_INT'] = df.P_INT - 1

(df.set_index(['TOUR_ID', 'ID', 'P_INT'])
.merge(df_last_availability[['AVAILABILITY']], how='left',
       left_index=True, right_index=True, suffixes=('', '_PREV'))
.reset_index()    
.drop(['P_INT'], axis=1))