基于先前行的pandas数据框列

时间:2020-06-12 16:35:59

标签: python pandas dataframe if-statement

我有一个下面的数据框

         id  action   
         ================
         10   CREATED   
         10   111
         10   222
         10   333
         10   DONE      
         10   222
         10   UPDATED   
         777  CREATED    
         10   333
         10   DONE      

我想创建一个新的“检查”列,该列将基于数据帧中前几行中的数据:

  1. 在操作列=“ DONE”中找到单元格
  2. 在DONE之前的前一行中搜索具有相同ID的第一个CREATED或UPDATED。如果CREATED为C,那么C为UPDATED为U。

输出:

         id  action   check
         ================
         10   CREATED   
         10   111
         10   222
         10   333
         10   DONE      C
         10   222
         10   UPDATED   
         777  CREATED    
         10   333
         10   DONE      U

我尝试使用多个if条件,但对我不起作用。您能帮忙吗?

3 个答案:

答案 0 :(得分:1)

考虑一个更复杂的示例数据框进行说明:

# print(df)
id  action   
10   CREATED   
10   111
10   222
10   333
10   DONE      
10   222
10   UPDATED   
777  CREATED    
10   333
10   DONE
777  DONE
10   CREATED
10   DONE
11   UPDATED
11   DONE     

使用:

transformer = lambda s: s[(s.eq('CREATED') | s.eq('UPDATED')).cumsum().idxmax()]

grouper = (
    lambda g: g.groupby(
        g['action'].eq('DONE').cumsum().shift().fillna(0))['action']
    .transform(transformer)
)

df['check'] = df.groupby('id').apply(grouper).droplevel(0).str[0]
df.loc[df['action'].ne('DONE'), 'check'] = ''

说明:

首先,我们在id上对数据帧进行分组,并应用grouper函数,然后对于每个分组的数据帧,我们通过在操作列中首次出现DONE对该分组的数据帧进行分组,因此从本质上讲,我们将分组的数据帧分为多个部分,每个部分之间的距离由操作列中的DONE值分开。然后我们使用transformer lambda函数根据操作列中CREATED值之前的第一个值(UPDATEDDONE)来变换每个分散的数据帧。

结果:

# print(df)
     id   action check
0    10  CREATED      
1    10      111      
2    10      222      
3    10      333      
4    10     DONE     C
5    10      222      
6    10  UPDATED      
7   777  CREATED      
8    10      333      
9    10     DONE     U
10  777     DONE     C
11   10  CREATED      
12   10     DONE     C
13   11  UPDATED      
14   11     DONE     U

答案 1 :(得分:0)

我不知道这是否是最好的答案,但我尝试创建自己的逻辑来解决此问题。

1)获取执行操作的行的索引:

m = df.groupby(['id'])['action'].transform(list).eq('DONE')
idx = df[m].index.values.tolist()

df [m]:

    id  action
4   10  DONE
9   10  DONE

idx:

[4, 9]

2)动作已创建或已更新的所有行的分组ID和索引

n = df.groupby(['id'])['action'].transform(list).str.contains('CREATED|UPDATED', case=False)

n_idx = df[n].index

df [n]:

    id  action
0   10  CREATED
6   10  UPDATED
7   777 CREATED

n_idx:

Int64Index([0, 6, 7], dtype='int64')

3)用空字符串填充新列“ check”:

df['check'] = ''

4)现在您有2个索引,一个用于DONE,另一个用于CREATED / UPDATED。 现在,您必须检查以前的行是否具有任何CREATED / UPDATED,并记住它们应该具有相同的ID。

ix = [0] + idx # <-- [0, 4, 9]
for a in list(zip(ix, ix[1:])): # <--- will create range (0,4), (4,9)
    for j in (n_idx):
        if j in range(a[0], a[1]): # <--- compare if CREATED/UPDATED indexes fall in this range. (checking previous row) and break if get any of them
            if (df.iloc[a[1]].id==df.iloc[j].id): # <--  check for id
                df.loc[a[1],'check'] = df.loc[j,'action'][0] # <--- assign Action
                break

最终输出:

df:

    id  action  check
0   10  CREATED 
1   10  111 
2   10  222 
3   10  333 
4   10  DONE    C
5   10  222 
6   10  UPDATED 
7   777 CREATED 
8   10  333 
9   10  DONE    U

完整代码:

m = df.groupby(['id'])['action'].transform(list).eq('DONE')
idx = df[m].index.values.tolist()
n = df.groupby(['id'])['action'].transform(list).str.contains('CREATED|UPDATED', case=False)
n_idx = df[n].index
ix = [0] + idx
df['check'] = ''

for a in list(zip(ix, ix[1:])):
    for j in (n_idx):
        if (j in range(a[0], a[1]+1)) and (df.iloc[a[1]].id==df.iloc[j].id):
            df.loc[a[1],'check'] = df.loc[j,'action'][0]
            break

具有结果的采样数据:

    id  action  check
0   10  CREATED 
1   10  111 
2   10  DONE    C
3   10  333 
4   10  DONE    
5   10  222 
6   10  UPDATED 
7   777 CREATED 
8   777 DONE    C
9   10  DONE    

    id  action  check
0   10  CREATED 
1   10  111 
2   10  DONE    C
3   10  333 
4   777 UPDATED 
5   10  222 
6   10  UPDATED 
7   777 CREATED 
8   777 DONE    U
9   10  DONE    

答案 2 :(得分:0)

一个有环的解决方案,不是最佳解决方案,但是可以完成工作。

这假定数据框中的行是按时间排序的,并且您有一个包含2列['id', 'action']和整数索引= range(N)的数据框,其中N是列数。然后:

df['check'] = ''
for i, action in zip(df.index, df['action']):
    if action == 'DONE':
        action_id = df.loc[i, 'id']
        prev_action = df.iloc[:i].loc[(df['id'] == action_id) & 
                      (df['action'].isin(['CREATED', 'UPDATED'])), 'action'].iloc[-1]
        if prev_action == 'CREATED':
            df.loc[i, 'check'] = 'C'
        elif prev_action == 'UPDATED':
            df.loc[i, 'check'] = 'U'

基本上,我们遍历动作,查找df['action'] == 'DONE'时的情况,然后获取与该动作相关联的ID,然后通过调用{查看当前'DONE'事件之前该ID的动作历史{1}}。然后,我们将该列表缩小为属于df.iloc[:i]的动作,然后查看该列表中的最后一个动作,并根据该动作将值分配给['CREATED', 'UPDATED']列。