如其他人所指出的,由于算法与生成行之间存在依赖关系,因此不存在纯粹的“单遍”向量化解决方案。但是,您仍然可以采用“收缩”窗口的方法来解决此问题,同时尽量减少所需的迭代步骤。
import pandas as pd
from pandas import NA
df = pd.DataFrame(
{'trade': [100, NA, NA, 101, NA, 102, NA, 98, 107, NA, 101, NA, 98, NA, NA, 94]}
).astype({'trade': 'Int32'})
tmp = df.dropna()
valid = [0] # 初始化有效索引列表
while valid[-1] < tmp.index[-1]:
# 获取所有未处理数据的窗口
chunk = tmp.loc[valid[-1]:, 'trade']
target = chunk.iloc[0] # 获取当前窗口的第一个有效值作为参考值
# 找到当前窗口中第一个不在目标值0.95至1.05倍范围内的索引
valid.append(chunk.between(target * 0.95, target * 1.05).idxmin())
print(
f'{valid = }', # 输出有效边界索引列表,如:[0, 8, 10, 15],表示while循环执行了len(valid)次
df.assign( # 根据valid中的索引给cleaned列赋值
cleaned=lambda d: d['trade'].where(d.index.isin(valid)),
),
sep='\n\n',
)
输出结果说明:
# valid = [0, 8, 10, 15] 表示确定的有效值边界索引
#
# trade cleaned
# 0 100 100 # 第一个有效值
# 1 <NA> <NA> # 不在有效范围内
# ... ... ...
# 7 98 <NA> # 不是新组的起始
# 8 107 107 # 新的有效值开始
# ... ... ...
# 10 101 101 # 另一个有效值开始
# ... ... ...
# 15 94 94 # 最后一个有效值
这个解决方案被称为“收缩窗口”方法,因为在每次while循环迭代中,我们处理的数据帧部分都会减小,直到处理完整个数据集。这样,我们可以尽可能多地利用DataFrame和Series的方法,避免在Python级别进行数据处理,从而提高效率。