Pandas rolling函数窗口取值异常 计算结果与预期不符问题
pandas rolling计算结果与预期不符问题排查
问题复现
现有如下样本数据(日期为索引,字段为收盘价close):
close date 2018-11-13 192.23 2018-11-12 194.17 2018-11-09 204.47 2018-11-08 208.49 2018-11-07 209.95 2018-11-06 203.77 2018-11-05 201.59 2018-11-02 207.48 2018-11-01 222.22 2018-10-31 218.86 2018-10-30 213.30 2018-10-29 212.24 2018-10-26 216.30 2018-10-25 219.80 2018-10-24 215.09 ...
设置参数:
resampleStr = '2D'
执行计算代码:
res = pd.concat([ df['close'].rolling(wind).apply(lambda x : (x[-1] - x[-0]) / x[-1]).reset_index(), df.reset_index()['date'].shift(-wind).rename('T-' + resampleStr), df.reset_index()['close'].rename('today'), df.reset_index()['close'].shift(-wind).rename('T-' + resampleStr) ], axis=1 ) res = res.dropna() print(res)
输出结果和手动计算预期偏差明显:比如首个滚动窗口手动计算值应为(194.17 - 208.49) / 208.49 = -0.06869,但实际输出值为-0.009991。
实际输出片段:
date close T-2D today T-2D 1 2018-11-12 -0.009991 2018-11-08 194.17 208.49 2 2018-11-09 -0.050374 2018-11-07 204.47 209.95 3 2018-11-08 -0.019282 2018-11-06 208.49 203.77 4 2018-11-07 -0.006954 2018-11-05 209.95 201.59 5 2018-11-06 0.030328 2018-11-02 203.77 207.48 6 2018-11-05 0.010814 2018-11-01 201.59 222.22 7 2018-11-02 -0.028388 2018-10-31 207.48 218.86 8 2018-11-01 -0.066331 2018-10-30 222.22 213.30 9 2018-10-31 0.015352 2018-10-29 218.86 212.24 10 2018-10-30 0.026067 2018-10-26 213.30 216.30 11 2018-10-29 0.004994 2018-10-25 212.24 219.80 12 2018-10-26 -0.018770 2018-10-24 216.30 215.09 13 2018-10-25 -0.015924 2018-10-23 219.80 222.73 14 2018-10-24 0.021898 2018-10-22 215.09 220.65
排查过程
在lambda函数中添加打印逻辑:lambda x : print(x) or后接原有计算逻辑,打印出的传入窗口片段如下,可见rolling取到的数据和预期不一致:
2018-11-13 192.23 2018-11-12 194.17 dtype: float64 date 2018-11-12 194.17 2018-11-09 204.47 dtype: float64 date 2018-11-09 204.47 2018-11-08 208.49 dtype: float64 date 2018-11-08 208.49 2018-11-07 209.95 dtype: float64 date 2018-11-07 209.95 2018-11-06 203.77 dtype: float64 date 2018-11-06 203.77 2018-11-05 201.59 dtype: float64 date 2018-11-05 201.59 2018-11-02 207.48 dtype: float64 date 2018-11-02 207.48 2018-11-01 222.22 dtype: float64 date 2018-11-01 222.22 2018-10-31 218.86 dtype: float64 date 2018-10-31 218.86 2018-10-30 213.30 dtype: float64 date 2018-10-30 213.30 2018-10-29 212.24 dtype: float64 date 2018-10-29 212.24 2018-10-26 216.30 dtype: float64 date 2018-10-26 216.3 2018-10-25 219.8 dtype: float64 date 2018-10-25 219.80 2018-10-24 215.09 dtype: float64
问题原因
这不是pandas的功能bug,是代码逻辑存在三个错误:
- rolling参数传错:定义了
resampleStr='2D',但调用rolling(wind)时传入的是整数2,此时rolling默认按行位置取数,窗口大小为2即取当前行+上1行共2行数据,完全不会按自然日2天的规则匹配时间,也不会跳过休市导致的日期间隔。比如2018-11-05和2018-11-02中间隔了周末休市,两个日期间差3天,但因为是相邻两行,依然会被分到同一个窗口。 - lambda索引写错:Python中
x[-0]等价于x[0],代表窗口内的第一个元素,x[-1]才是窗口内最后一个元素(当前行价格)。第一个窗口取到的是11-13的192.23和11-12的194.17,代入公式计算(192.23 - 194.17)/192.23 ≈ -0.01009,和输出的-0.009991的差异是浮点计算精度导致,结果完全匹配,根本没有取到预期的11-08的价格。 - 逻辑不匹配:后续用
shift(-wind)是按行向上位移2行取数,和rolling按相邻行取数的规则完全不一致,导致拼接出来的T-2D价格和rolling实际用的价格不是同一条数据,结果自然和预期对不上。
修复方案
- 如果要按自然日2天的时间窗口计算:先确保索引是datetime类型,直接把时间偏移字符串传给rolling,不要用整数窗口:
df.index = pd.to_datetime(df.index) # 按2天时间范围取窗口内所有数据计算 res = df['close'].rolling('2D').apply(lambda x: (x[-1] - x[0])/x[0])
- 如果要按2个交易日间隔计算:不需要用低效的rolling.apply,直接用shift做向量计算即可:
wind = 2 df['ret'] = (df['close'] - df['close'].shift(wind)) / df['close'].shift(wind)
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

