roll_mean熊猫不稳定

时间:2014-09-30 21:31:47

标签: python numpy pandas

我正在从我们当前的环境(Python 2.7.3 64位,pandas 0.9)升级到新的(Python 2.7.6,pandas 0.14.1)并且我的一些回归测试失败了。我将其追溯到pandas.stats.moments.rolling_mean

的行为

以下是重现错误的示例:

import pandas as pd
data = [
    1.0,
    0.99997000000000003,
    0.99992625131299995,
    0.99992500140499996,
    0.99986125618599997,
    0.99981126312299995,
    0.99976377208800005,
    0.99984375318999996]
ser = pd.Series(data, index=pd.date_range('2008-05-28', '2008-06-06', freq='B'))

print "rolling mean: %.17f" % pd.stats.moments.rolling_mean(ser, window=5, min_periods=1)['2008-06-06']
print "sum divide:   %.17f" % (ser['2008-6-1':'2008-6-6'].sum()/5)

在我的原始环境中,我得到以下输出:

rolling mean: 0.99984100919839991                                                   
sum divide:   0.99984100919839991

但在我的新环境中输出现在是:

rolling mean: 0.99984100919840002                                                   
sum divide:   0.99984100919839991

正如您所看到的,滚动平均值现在给出的数字略有不同。这肯定是一个小小的差异,但错误变得复杂,最终变得非常重要。

有谁知道可能导致它的原因或是否有解决方法?

1 个答案:

答案 0 :(得分:4)

不同方法的结果差异的原因是累积的舍入误差在和除计算期间更大。在过去,滚动均值计算遇到了类似的问题,但似乎其算法在过去几个版本中的内部改进使其更精确。

首先,让我们确定新的滚动平均值结果更精确。我们将通过两次调用 sum divide 方法来实现,但每次都有不同的精度:

In [166]: ser1 = pd.Series(data, index=pd.date_range('2008-05-28', '2008-06-06', freq='B'))

In [167]: type(ser1[0])
Out[167]: numpy.float64

In [168]: print "sum divide:   %.17f" % (ser1['2008-6-1':'2008-6-6'].sum()/5)
sum divide:   0.99984100919839991

In [169]: ser2 = pd.Series(data, index=pd.date_range('2008-05-28', '2008-06-06', freq='B'), dtype = np.float128)

In [170]: print "sum divide:   %.17f" % (ser2['2008-6-1':'2008-6-6'].sum()/5)
sum divide:   0.99984100919840002

使用更大的np.float128精度会产生更接近新滚动平均值版本的值。这清楚地证明了新的滚动平均值版本比前一版本更精确。

这也为您的问题提供了一种可能的解决方法 - 通过定义用于保存np.float128对象的系列,在计算中使用更高的精度。这提高了除数方法的精度,但不影响滚动均值方法的精度:

In [185]: pd.stats.moments.rolling_mean(ser1, window=5, min_periods=1) == pd.stats.moments.rolling_mean(ser2, window=5, min_periods=1)
Out[185]: 
2008-05-28    True
2008-05-29    True
2008-05-30    True
2008-06-02    True
2008-06-03    True
2008-06-04    True
2008-06-05    True
2008-06-06    True
Freq: B, dtype: bool

请注意,尽管这会使每种方法的结果更加接近,但它们看起来完全相同:

In [194]: print "sum divide:   %.60f" % (ser2['2008-6-1':'2008-6-6'].sum()/5)
sum divide:   0.999841009198400021418251526483800262212753295898437500000000

In [195]: print "rolling mean: %.60f" % pd.stats.moments.rolling_mean(ser2, window=5, min_periods=1)['2008-06-06']
rolling mean: 0.999841009198400021418251526483800262212753295898437500000000
从处理器的角度来看,它们仍然存在差异:

In [196]: pd.stats.moments.rolling_mean(ser2, window=5, min_periods=1)['2008-06-06'] == ser2['2008-6-1':'2008-6-6'].sum()/5
Out[196]: False

In [197]: pd.stats.moments.rolling_mean(ser2, window=5, min_periods=1)['2008-06-06'] - ser2['2008-6-1':'2008-6-6'].sum()/5
Out[197]: 4.4398078963281406573e-17

但希望现在的误差幅度在你的用例范围内。