python - Pandas 的 rolling_mean 不稳定性

标签 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

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

有谁知道可能是什么原因导致的,或者是否有解决方法?

最佳答案

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

首先,让我们确定新的滚动平均值 结果更加精确。我们将通过两次调用和除方法来完成此操作,但每次使用不同的精度:

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/

相关文章:

python - numpy数组操作方法

Python Pandas 合并数据帧而不重复列

python - pandas.DataFrame 构造函数中不允许元组的元组

python - 使用 C++ Eigen 库处理 numpy 数组后输出错误

python - 从 python 脚本中检查 python 版本,并基于它检查 if/else

python - Pandas .groupby 自动选择列

python - 在 Azure API 上托管 Flask(python) API

python - 在python中按纬度和经度从.nc文件中提取数据

python - 从多个文本文件中提取想要的词(Python 3.6)

python - 如果我在 MapReduce 作业结束时清理 Google 存储,为什么 BigQuery 不更新?