我正在从当前环境(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
如您所见,滚动平均值现在给出的数字略有不同。这肯定是一个很小的差异,但错误会变得复杂,最终变得非常重要。
有谁知道可能是什么原因导致的,或者是否有解决方法?
最佳答案
不同方法的结果不同的原因是在和除 计算期间累积的舍入误差更大。过去,滚动均值 计算也遇到过类似的问题,但似乎在过去几个版本中对其算法进行了内部改进,使其获得了更精确的结果。
首先,让我们确定新的滚动平均值 结果更加精确。我们将通过两次调用和除方法来完成此操作,但每次使用不同的精度:
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
但希望误差幅度(现在小了一点)在您的用例范围内。
关于python - Pandas 的 rolling_mean 不稳定性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26130442/