我正在从我们当前的环境(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
正如您所看到的,滚动平均值现在给出的数字略有不同。这肯定是一个小小的差异,但错误变得复杂,最终变得非常重要。
有谁知道可能导致它的原因或是否有解决方法?
答案 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
但希望现在的误差幅度在你的用例范围内。