Pandas returns 当有很多组时,float64 的 groupby 滚动零和不正确
Pandas returns incorrect groupby rolling sum of zeros for float64 when having many groups
当使用 dtype float64 在 pandas 中进行 groupby rolling 时,当组数很大时,零和变成任意小的浮点数。例如,
import pandas as pd
import numpy as np
np.random.seed(1)
df = pd.DataFrame({'a': (np.random.random(800)*1e5+1e5).tolist() + [0.0]*800, 'b': list(range(80))*20})
a = df.groupby('b').rolling(5, min_periods=1).agg({'a': 'sum'})
第一行生成一个包含 2 列 a
和 b
的数据框。
- 列
a
在 1e5
和 2e5
之间有 800 个随机数和 800 个零。
- 第
b
列将这些分配给 80 个组。
例如,对于第 79 组,df
如下所示:
a b
79 158742.001924 79
159 115045.502837 79
239 171582.695286 79
319 181072.123361 79
399 194672.826961 79
479 130100.794308 79
559 169784.165605 79
639 132752.405585 79
719 162355.180105 79
799 148140.045915 79
879 0.000000 79
959 0.000000 79
1039 0.000000 79
1119 0.000000 79
1199 0.000000 79
1279 0.000000 79
1359 0.000000 79
1439 0.000000 79
1519 0.000000 79
1599 0.000000 79
第二行计算每组 a
列的 5
的滚动总和。
人们会期望每组中最后几个条目的滚动总和为零,例如79.
但是,会返回任意小的浮点数,例如-5.820766e-11
下面第 79 组
a
79 1.587420e+05
159 2.737875e+05
239 4.453702e+05
319 6.264423e+05
399 8.211152e+05
479 7.924739e+05
559 8.472126e+05
639 8.083823e+05
719 7.896654e+05
799 7.431326e+05
879 6.130318e+05
959 4.432476e+05
1039 3.104952e+05
1119 1.481400e+05
1199 -5.820766e-11
1279 -5.820766e-11
1359 -5.820766e-11
1439 -5.820766e-11
1519 -5.820766e-11
1599 -5.820766e-11
如果我们将组数减少到 20
,问题就会消失。例如
df['b'] = df['b'] = list(range(20))*80
a = df.groupby('b').rolling(5, min_periods=1).agg({'a': 'sum'})
这产生(对于第 19 组,因为从 0-19 仅有 20 个组)
a
19 165083.125668
39 359750.793592
59 485563.758520
79 644305.760443
99 837370.199660
...
1519 0.000000
1539 0.000000
1559 0.000000
1579 0.000000
1599 0.000000
[80 rows x 1 columns]
这仅在 pandas 1.2.5/python 3.7.9/windows 10 上测试过。您可能需要增加组数才能显示,具体取决于在你的机器内存上。
在我的应用程序中,我无法真正控制组的数量。我可以将 dtype 更改为 float32
,问题就消失了。但是,这导致我对大数字失去了精度。
除了使用 float32
之外,知道是什么原因造成的以及如何解决它吗?
TLDR:这是优化的副作用;解决方法是使用 non-pandas 总和。
原因是pandas试图优化。天真的滚动 window 函数将花费 O(n*w) 时间。但是,如果我们知道该函数是求和,我们可以减去 window 中的一个元素并添加进入它的元素。这种方法不再依赖于 window 大小,并且总是 O(n).
需要注意的是,现在我们将得到浮点精度的副作用,其表现类似于您所描述的。
来源:Python code calling window aggregation, Cython implementation of the rolling sum
当使用 dtype float64 在 pandas 中进行 groupby rolling 时,当组数很大时,零和变成任意小的浮点数。例如,
import pandas as pd
import numpy as np
np.random.seed(1)
df = pd.DataFrame({'a': (np.random.random(800)*1e5+1e5).tolist() + [0.0]*800, 'b': list(range(80))*20})
a = df.groupby('b').rolling(5, min_periods=1).agg({'a': 'sum'})
第一行生成一个包含 2 列 a
和 b
的数据框。
- 列
a
在1e5
和2e5
之间有 800 个随机数和 800 个零。 - 第
b
列将这些分配给 80 个组。
例如,对于第 79 组,df
如下所示:
a b
79 158742.001924 79
159 115045.502837 79
239 171582.695286 79
319 181072.123361 79
399 194672.826961 79
479 130100.794308 79
559 169784.165605 79
639 132752.405585 79
719 162355.180105 79
799 148140.045915 79
879 0.000000 79
959 0.000000 79
1039 0.000000 79
1119 0.000000 79
1199 0.000000 79
1279 0.000000 79
1359 0.000000 79
1439 0.000000 79
1519 0.000000 79
1599 0.000000 79
第二行计算每组 a
列的 5
的滚动总和。
人们会期望每组中最后几个条目的滚动总和为零,例如79.
但是,会返回任意小的浮点数,例如-5.820766e-11
下面第 79 组
a
79 1.587420e+05
159 2.737875e+05
239 4.453702e+05
319 6.264423e+05
399 8.211152e+05
479 7.924739e+05
559 8.472126e+05
639 8.083823e+05
719 7.896654e+05
799 7.431326e+05
879 6.130318e+05
959 4.432476e+05
1039 3.104952e+05
1119 1.481400e+05
1199 -5.820766e-11
1279 -5.820766e-11
1359 -5.820766e-11
1439 -5.820766e-11
1519 -5.820766e-11
1599 -5.820766e-11
如果我们将组数减少到 20
,问题就会消失。例如
df['b'] = df['b'] = list(range(20))*80
a = df.groupby('b').rolling(5, min_periods=1).agg({'a': 'sum'})
这产生(对于第 19 组,因为从 0-19 仅有 20 个组)
a
19 165083.125668
39 359750.793592
59 485563.758520
79 644305.760443
99 837370.199660
...
1519 0.000000
1539 0.000000
1559 0.000000
1579 0.000000
1599 0.000000
[80 rows x 1 columns]
这仅在 pandas 1.2.5/python 3.7.9/windows 10 上测试过。您可能需要增加组数才能显示,具体取决于在你的机器内存上。
在我的应用程序中,我无法真正控制组的数量。我可以将 dtype 更改为 float32
,问题就消失了。但是,这导致我对大数字失去了精度。
除了使用 float32
之外,知道是什么原因造成的以及如何解决它吗?
TLDR:这是优化的副作用;解决方法是使用 non-pandas 总和。
原因是pandas试图优化。天真的滚动 window 函数将花费 O(n*w) 时间。但是,如果我们知道该函数是求和,我们可以减去 window 中的一个元素并添加进入它的元素。这种方法不再依赖于 window 大小,并且总是 O(n).
需要注意的是,现在我们将得到浮点精度的副作用,其表现类似于您所描述的。
来源:Python code calling window aggregation, Cython implementation of the rolling sum