本文介绍一种针对金融时间序列数据的特殊填充逻辑——当某行中任一指定列值与上一行相同,则该行所有目标列统一回填为上一行的有效值,需通过迭代方式精确实现。
本文介绍一种针对金融时间序列数据的特殊填充逻辑——当某行中任一指定列值与上一行相同,则该行所有目标列统一回填为上一行的有效值,需通过迭代方式精确实现。
在金融数据分析中,常遇到价格序列中出现“平台期”(即某资产价格连续多日不变),但基准(Benchmark)价格可能仍在变动;反之亦然。此时若希望保持两列变化的一致性——即只要任一列未发生变化,就将整行目标列同步回填为前一个有效状态——这种逻辑无法通过 fillna()、ffill() 或 replace() 等向量化操作直接完成,因为其依赖动态更新后的前一行状态,具有内在迭代性(例如:2023-12-24 行的 Benchmark_Price=100.0 是否被替换,取决于 2023-12-23 行经处理后是否已变为 98.0)。
因此,必须采用显式循环,逐行判断并实时更新。以下是推荐实现方案:
✅ 核心逻辑说明
- 遍历 DataFrame 的每一行;
- 将当前行与「上一行处理后的结果」逐元素比较;
- 若存在任意一列值未变化(即 row == prev 在至少一列成立),则将当前行中所有不匹配的列,强制设为上一行对应值(即“全行对齐”);
- 更新 prev 为当前处理后的新行,供下一轮使用。
? 完整可运行代码
import pandas as pd
import numpy as np
def bidirectional_forward_fill(arr):
"""
对二维 NumPy 数组执行双向前向填充:
若当前行任一元素等于上一行对应元素,则整行目标列均继承上一行值。
Parameters:
arr : np.ndarray, shape (n_rows, n_cols)
Returns:
np.ndarray, same shape as input, in-place modified
"""
if arr.shape[0] == 0:
return arr
prev = arr[0].copy() # 初始化为第一行原始值
for i in range(1, len(arr)):
row = arr[i].copy()
# 判断哪些列发生了变化
changed = row != prev
# 若存在未变化的列(即 not changed.all()),则整行按 prev 同步
if not changed.all():
arr[i] = prev # 关键:整行覆写为前一行处理后状态
prev = arr[i] # 更新 prev 为当前已修正行
return arr
# 构造示例数据
df = pd.DataFrame({
'date': pd.date_range('2023-12-20', periods=12, freq='D'),
'Security_Price': [100.0, 105.0, 110.0, 110.0, 112.0, 112.0, 112.0, 115.0, 118.0, 120.0, 120.0, 107.0],
'Benchmark_Price': [100.0, 95.0, 98.0, 100.0, 100.0, 100.0, 105.0, 107.0, 107.0, 107.0, 110.0, 108.0]
})
# 应用双向填充逻辑(仅作用于指定列)
cols = ['Security_Price', 'Benchmark_Price']
df[cols] = bidirectional_forward_fill(df[cols].to_numpy())
print(df)
⚠️ 注意事项
- 不可向量化替代:shift() + mask() 或 where() 组合无法满足该需求,因其无法捕获“前一行已被修改”的链式效应;
- 顺序敏感:必须严格按行序从前向后处理,反向或分块处理将导致结果错误;
- 性能提示:对于超大规模数据(>1M 行),该循环在 Python 层略慢,但逻辑清晰、易于调试;如需加速,可考虑用 Numba 编译该函数;
- 扩展性好:函数支持任意数量的目标列,只需传入对应列组成的数组即可。
该方法确保了时间序列中“变化同步性”,特别适用于构建一致的相对收益指标、归一化价格曲线或风控信号对齐场景。











