我有一个来自外部来源(x
)的系列。它都是正面的,大部分都是零。
x.describe()
count 23275.000000
mean 0.015597
std 0.411720
min 0.000000
25% 0.000000
50% 0.000000
75% 0.000000
max 26.000000
dtype: float64
但是,在其上运行rolling_sum
会产生小于零的值。为什么会这样? 有什么方法可以避免/绕过它?
rolling_sum(x, window=100).iloc[-1]
-1.4743761767e-13
(rolling_sum(x, window=100)<0).sum()
16291
甚至更奇怪的是,这两个计算(据我所知,应该产生相同的值)不会:
rolling_sum(x, window=100).iloc[-1]
-1.4743761767e-13
rolling_sum(x.iloc[-100:], window=100).iloc[-1]
0.0
(这是大熊猫0.14.1和0.15.2)
答案 0 :(得分:6)
我想我可以猜到发生了什么:
In [481]: df=pd.DataFrame( { 'x':[0,0,.1,.2,0,0] } )
In [482]: df2 = pd.rolling_sum(df,window=2)
In [483]: df2
Out[483]:
x
0 NaN
1 0.000000e+00
2 1.000000e-01
3 3.000000e-01
4 2.000000e-01
5 2.775558e-17
看起来不错,除了最后一个,对吗?事实上,四舍五入模糊了一些其他条目并不像第一眼看上去那么干净。只是默认的显示格式会伪装成这个,除非你的值非常接近于零。
In [493]: for i in range(6):
...: print '%22.19f' % df2.ix[i,'x']
nan
0.0000000000000000000
0.1000000000000000056
0.3000000000000000444
0.2000000000000000389
0.0000000000000000278
这里发生的事情是,rolling_sum每次都不会真正做到新的总和。相反,它将通过添加最新的数字并删除最旧的数字来更新总和。在使用window=2
的这个简单示例中,这将没有用,但是如果窗口大得多,那么可以大大加快计算速度,因此以这种方式执行它是有意义的。
然而,这意味着可能会发生一些意想不到的结果。你期望最后的滚动总和是0+0
的结果,但事实并非如此,它实际上是这样的:
In [492]: (.0+.0)+(.1-.0)+(.2-.0)+(.0-.1)+(.0-.2)
Out[492]: 2.7755575615628914e-17
结论:你的结果基本上没问题。它恰好发生在您使用它的方式(使用这些数据)揭示了这些事物固有的潜在精确问题。这种情况发生了很多,但默认显示通常会隐藏在第13个小数位发生的事情。
编辑添加:根据Korem的评论,小的负数实际上是一个问题。我认为在这种情况下最好的办法是使用numpy的around
函数并将上面的第二步替换为:
df2 = np.around(pd.rolling_sum(df,window=2),decimals=5)
这会强制所有小数字(正数或负数)为零。我认为这是一个非常安全的通用解决方案。如果你的所有数据都有整数值,你可以重新整数,但这显然不是一般的解决方案。
答案 1 :(得分:0)
pd.rolling()方法也存在此问题,并且如果在高精度的相对较小的值列表中包含大的正整数,也会发生此问题。
import pandas as pd
x = pd.DataFrame([0, 1, 2, 2.23425304, 3.2342352934, 4.32423857239])
x.rolling(window=2).mean()
0
0 NaN
1 0.500000
2 1.500000
3 2.117127
4 2.734244
5 3.779237
用1E15替换第二个元素...
x = pd.DataFrame([0, 1, 1E15, 2.23425304, 3.2342352934, 4.32423857239])
x.rolling(window=2).mean()
0
0 NaN
1 5.000000e-01
2 5.000000e+14
3 5.000000e+14
4 2.750000e+00
5 3.794993e+00
滚动标准偏差更明显...
x = pd.DataFrame([0, 1, 2, 2.23425304, 3.2342352934, 4.32423857239])
x.rolling(window=2).std()
0
0 NaN
1 0.707107
2 0.707107
3 0.165642
4 0.707094
5 0.770749
x = pd.DataFrame([0, 1, 1E15, 2.23425304, 3.2342352934, 4.32423857239])
x.rolling(window=2).std()
0
0 NaN
1 7.071068e-01
2 7.071068e+14
3 7.071068e+14
4 1.186328e+07
5 1.186328e+07
唯一的解决方案似乎是为了牺牲准确性而牺牲了性能,即直接进行滚动平均。
def rolling_window_slow(window, df):
df_mean = []
for i in range(len(df) - window):
df_mean.append(df.iloc[i:i+window, :].mean())
return df_mean