问题代码:
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-2
col_1值与t-1
col_2值,以决定分配给t
col_2值的内容。因此col_1是静态的,而col_2时间t
的值每次迭代都会更新。
好吧,itertuples
或apply
不是特别快,看看这个答案。您的主要问题是您在循环中多次访问单元格,并在每次循环中为单元格赋值。通过循环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 ms
vs.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