我认为你可以使用shift 和mask:
mask = ((df.column_A == df.column_A.shift())
& (df.column_B == 'START') & (df.column_B.shift() == 'STOP'))
df.loc[mask, 'time'] -= df.time.shift().loc[mask]
掩码选择'column_A'中的值等于前一个值的行(由shift获得)并且'column_B'等于'START'并且上一行'STOP'。使用loc 允许您通过在列时间中使用相同的掩码删除前一行的值(再次shift)来通过mask 在列“时间”中更改所有选定行的值
编辑:举个例子:
df = pd.DataFrame({'column_A': [0,1,1,2,1,2,2], 'column_B': ['START', 'STOP', 'START','STOP', 'START','STOP', 'START'], 'time':range(7)})
column_A column_B time
0 0 START 0
1 1 STOP 1
2 1 START 2
3 2 STOP 3
4 1 START 4
5 2 STOP 5
6 2 START 6
所以这里第 2 行和第 6 行满足您的条件,因为前一行在 column_A 中具有相同的值,并且在 column_B 中获得“START”,而前一行具有“STOP”。
运行代码后你会得到df:
column_A column_B time
0 0 START 0.0
1 1 STOP 1.0
2 1 START 1.0
3 2 STOP 3.0
4 1 START 4.0
5 2 STOP 5.0
6 2 START 1.0
第 2 行的时间值为 1(最初是 2 减去前第 1 行的值),第 6 行相同(6 - 5)
编辑时间比较让我们创建一个 3000 行的 df
df = pd.DataFrame( [['A', 'START', 3], ['A', 'STOP', 6], ['B', 'STOP', 2],
['C', 'STOP', 1], ['C', 'START', 9], ['C', 'STOP', 7]],
columns=['column_A', 'column_B', 'time'] )
df = pd.concat([df]*500)
df.shape
Out[16]: (3000, 3)
现在用两种方法创建两个函数:
# original method
def espogian (df):
N = df.shape[0]
for i in range(1, N):
if df.column_A.iloc[i] == df.column_A.iloc[i-1]:
if df.column_B.iloc[i] == 'START' and df.column_B.iloc[i-1] == 'STOP':
df.time.iloc[i] = df.time.iloc[i] - df.time.iloc[i-1]
return df
# mine
def ben(df):
mask = ((df.column_A == df.column_A.shift())
& (df.column_B == 'START') & (df.column_B.shift() == 'STOP'))
df.loc[mask, 'time'] -= df.time.shift().loc[mask]
return df
然后运行timeit:
%timeit espogian (df)
1 loop, best of 3: 8.71 s per loop
%timeit ben (df)
100 loops, best of 3: 4.79 ms per loop
# verify they are equal
df1 = espogian (df)
df2 = ben (df)
(df1==df2).all()
Out[24]:
column_A True
column_B True
time True