对于pandas,如何加速需要使用前一行计算值的迭代?



问题代码:

import pandas as pd
df = pd.DataFrame(
[
list(range(200)),
list(range(200, 400))
],
index=['col_1', 'col_2']
).transpose()
col_1_index = df.columns.get_loc('col_1')
col_2_index = df.columns.get_loc('col_2')
target_1 = 2
for i in range(2, len(df)):
if (
df.iloc[i - 2, col_1_index] -
df.iloc[i - 1, col_2_index]
) > target_1:
col_2_value = (
df.iloc[i - 1, col_2_index] +
target_1
)
elif (
df.iloc[i - 1, col_2_index] -
df.iloc[i - 2, col_1_index]
) > target_1:
col_2_value = (
df.iloc[i - 1, col_2_index] -
target_1
)
else:
col_2_value = df.iloc[i - 2, col_1_index]
df.iloc[i, col_2_index] = col_2_value
df
'''
# expected output
col_1 col_2
0   0    200
1   1    201
2   2    199
3   3    197
4   4    195
... ...  ...
195 195  193
196 196  194
197 197  195
198 198  196
199 199  197
'''

我的问题是我不能使用加速迭代的常见方法,如df.itertuples()df.apply(),因为我引用了前一行的计算值。


逻辑在DataFrame上迭代,比较t-2col_1值与t-1col_2值,以决定分配给tcol_2值的内容。因此col_1是静态的,而col_2时间t的值每次迭代都会更新。

好吧,itertuplesapply不是特别快,看看这个答案。您的主要问题是您在循环中多次访问单元格,并在每次循环中为单元格赋值。通过循环col_1中的值并使用一个变量来保持先前计算的值,append将结果保存在列表中(比分配给特定单元格更快),并且在循环之外立即分配列,可以提高效率。有一种方法:

prev_val2 = df.iloc[1, col_2_index] 
l_val2 = [df.iloc[0, col_2_index], prev_val2] #for the results
for val1 in df['col_1'].to_numpy()[:-2]:
if (val1-prev_val2)>target_1:
prev_val2 += target_1
elif (prev_val2-val1)>target_1:
prev_val2 -= target_1
else:
prev_val2 = val1
l_val2.append(prev_val2)
df['col_2_fast'] = l_val2 #assign outside of the loop 

现在在200行数据帧上,速度比较从61.5 ms0.380 ms使用这个(大约快160倍),增益应该随着数据帧的大小而增加。当然,在运行了你的方法和这个方法之后,df['col_2'].eq(df['col_2_fast']).all()给出了True

我通过采用@nickodell建议将前一个值保存为变量来解决这个问题。

原始代码与优化代码的时间对比如下:

CPU times: total: 46.9 msvs.CPU times: total: 15.6 ms

import pandas as pd
def calc(df):
col_1_tmin2_val = df['col_1'][0]
col_1_tmin1_val = df['col_1'][1]
col_2_tmin1_val = df['col_2'][1]

def func_apply(row: pd.Series) -> float:
nonlocal col_1_tmin2_val
nonlocal col_1_tmin1_val
nonlocal col_2_tmin1_val
if (col_1_tmin2_val - col_1_tmin1_val) > target_1:
col_2_value = (col_2_tmin1_val + target_1)
elif (col_2_tmin1_val - col_1_tmin2_val) > target_1:
col_2_value = (col_2_tmin1_val - target_1)
else:
col_2_value = col_1_tmin2_val
col_1_tmin2_val = col_1_tmin1_val
col_1_tmin1_val = row['col_1']
col_2_tmin1_val = col_2_value
return col_2_value
df.iloc[2:, col_2_index] = df.iloc[2:].apply(lambda x: func_apply(x), axis=1)
df = pd.DataFrame(
[
list(range(200)),
list(range(200, 400))
],
index=['col_1', 'col_2']
).transpose()
col_2_index = df.columns.get_loc('col_2')
target_1 = 2

calc(df)

df

相关内容

  • 没有找到相关文章

最新更新