You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于前一行与其他列,用lambda函数计算Pandas列值

高效计算Pandas中依赖前序值的列(10K行规模)

问题描述

现有如下Pandas DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'up_walk': [0.000000, 1.550781, 0.957031, 0.000000],
    'down_walk': [17.827148, 0.000000, 0.000000, 2.878906],
    'up_avg': [0.36642, np.nan, np.nan, np.nan],
    'down_avg': [9.06815, np.nan, np.nan, np.nan]
})

需要通过递推公式填充up_avg和down_avg的NaN值:

  • up_avg[i] = up_avg[i-1] * 12 + up_walk[i]
  • down_avg[i] = down_avg[i-1] * 12 + down_walk[i]

数据规模达10K行,纯Python循环速度过慢,尝试用apply结合lambda函数报错:

df['up_avg'] = df.apply(lambda x: pd.Series(np.where((x.up_avg != None), x.up_avg.shift() * 12 + x.up_walk, x.up_avg)))

错误信息:AttributeError: 'Series' object has no attribute 'up_avg'

预期最终DataFrame:

index up_walk    down_walk   up_avg      down_avg
0     0.000000   17.827148  0.36642    9.06815
1     1.550781    0.000000  5.947821  108.8178
2     0.957031    0.000000  72.330883 1305.8136
3     0.000000    2.878906  867.970596 15672.642106

错误原因分析

  • apply(lambda x)中的x是单行的Series对象,没有shift()方法,也不能直接调用x.up_avg(x本身就是单行的所有列值),因此触发属性错误。
  • 核心问题:apply是逐行独立处理的,无法传递前一行计算出的up_avg结果,完全不适合处理这种依赖前序值的递推计算。

高效解决方案

方案1:矢量化数学计算(最快,推荐)

递推公式a_n = 12*a_{n-1} + b_n可转化为数学求和式:
$$a_n = 12^n \cdot a_0 + \sum_{k=1}^n b_k \cdot 12^{n-k}$$
利用numpy广播机制实现全量计算,完全避免循环:

# 处理up_avg
n = len(df)
# 生成12的幂次数组:12^0,12^1,...,12^(n-1)
powers_of_12 = 12 ** np.arange(n)
# 计算每个up_walk项对应的权重
weights = powers_of_12[-1] / powers_of_12
# 计算求和部分
sum_part = np.cumsum(df['up_walk'] * weights) / weights
# 填充并计算最终up_avg
df['up_avg'] = df['up_avg'].fillna(0)
df['up_avg'] = df['up_avg'].iloc[0] * powers_of_12 + sum_part - df['up_walk'].iloc[0] * weights[0]

# 同理处理down_avg
sum_part_down = np.cumsum(df['down_walk'] * weights) / weights
df['down_avg'] = df['down_avg'].fillna(0)
df['down_avg'] = df['down_avg'].iloc[0] * powers_of_12 + sum_part_down - df['down_walk'].iloc[0] * weights[0]

验证结果:

print(df.round(6))

输出与预期完全一致,10K行数据计算耗时仅毫秒级。

方案2:Numba加速循环(比纯Python循环快100+倍)

若对数学推导不熟悉,用Numba装饰器将Python代码编译为机器码,性能接近矢量化:

from numba import jit

@jit(nopython=True)
def fill_recursive(arr_initial, arr_walk, multiplier):
    n = len(arr_initial)
    result = arr_initial.copy()
    for i in range(1, n):
        result[i] = result[i-1] * multiplier + arr_walk[i]
    return result

# 转换为numpy数组传入计算
df['up_avg'] = fill_recursive(df['up_avg'].values, df['up_walk'].values, 12)
df['down_avg'] = fill_recursive(df['down_avg'].values, df['down_walk'].values, 12)

10K行的循环耗时约几毫秒,远快于纯Python循环。

方案3:Pandas expanding窗口(不推荐,性能一般)

用expanding().apply()实现,但本质仍是循环,性能不如前两个方案,仅作参考:

def recursive_calc(window):
    if len(window) == 1:
        return window.iloc[0]
    return recursive_calc(window[:-1]) * 12 + window.iloc[-1]

# 构造包含初始值和up_walk的序列
combined_up = pd.Series([df['up_avg'].iloc[0]] + df['up_walk'].tolist()[1:])
df['up_avg'] = combined_up.expanding().apply(recursive_calc)

# 同理处理down_avg
combined_down = pd.Series([df['down_avg'].iloc[0]] + df['down_walk'].tolist()[1:])
df['down_avg'] = combined_down.expanding().apply(recursive_calc)

总结

  • 优先选择矢量化数学计算,性能最优,适合大规模数据;
  • Numba加速循环是次优选择,代码直观,性能接近矢量化;
  • 绝对不要用apply(lambda)处理这类递推问题,既无法实现需求,又效率低下。

内容的提问来源于stack exchange,提问作者Vladimir Shulman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:35:24