pandas中的.sum()方法会产生不一致的结果

时间:2017-10-27 07:56:00

标签: python pandas sum

我有一个大型DataFrame(大约4e + 07行)。

总结时,我得到 2显着不同的结果我是否在列选择之前或之后总和。 此外,类型从float32更改为float64,即使总数都低于2 ** 31

df[[col1, col2, col3]].sum()
Out[1]:
col1         9.36e+07
col2         1.39e+09
col3         6.37e+08
dtype: float32

df.sum()[[col1, col2, col3]]
Out[2]:
col1         1.21e+08
col2         1.70e+09
col3         7.32e+08
dtype: float64

我显然错过了什么,有没有人有同样的问题?

感谢您的帮助。

2 个答案:

答案 0 :(得分:4)

要了解这里发生了什么,你需要了解熊猫正在做什么。我要简化一下,因为有很多花里胡哨和特殊情况要考虑,但大致看起来像这样:

假设您有一个带有各种数字列的Pandas DataFrame对象df(我们将忽略日期时间列,分类列等)。当你计算df.sum()时,Pandas:

  1. Extracts the values将数据帧转换为二维NumPy数组。
  2. Applies the NumPy sum function到具有axis=0的二维数组,以计算列总和。
  3. 这是重要的第一步。 DataFrame的列可能具有不同的dtypes,但是2d NumPy数组只能有一个dtype。如果df混合了float32int32列(例如),Pandas必须同时选择适合两个列的单个dtype,在这种情况下,它会选择{{1 }}。因此,当计算总和时,它使用双精度算法在双精度值上计算。这是你的第二个例子中发生的事情。

    另一方面,如果您首先减少到float64列,那么Pandas可以并将使用float32 dtype作为2d NumPy数组,因此{{ 1}}计算以单精度执行。这就是你的第一个例子中发生的事情。

    以下是一个显示此操作的简单示例:我们将分别设置一个包含1百万行和三列dtypes float32sumfloat32的DataFrame。所有的值都是:

    float32

    现在,当我们直接计算总和时,Pandas首先将所有内容都转换为int32 s。对于所有三列,使用>>> import numpy as np, pandas as pd >>> s = np.ones(10**8, dtype=np.float32) >>> t = np.ones(10**8, dtype=np.int32) >>> df = pd.DataFrame(dict(A=s, B=s, C=t)) >>> df.head() A B C 0 1.0 1.0 1 1 1.0 1.0 1 2 1.0 1.0 1 3 1.0 1.0 1 4 1.0 1.0 1 >>> df.dtypes A float32 B float32 C int32 dtype: object 类型也进行了计算,我们得到了准确的答案。

    float64

    但是,如果我们首先将数据帧减少到float64列,那么>>> df.sum() A 100000000.0 B 100000000.0 C 100000000.0 dtype: float64 - 算术用于总和,我们得到的答案非常差。

    float32

    不准确当然是由于使用的dtype对于有问题的任务没有足够的精确度:在求和的某个时刻,我们最终反复将float32添加到>>> df[['A', 'B']].sum() A 16777216.0 B 16777216.0 dtype: float32 ,每次都获得1.0,感谢usual floating-point problems。解决方案是在进行计算之前自己明确地转换为16777216.0

    然而,这并不是熊猫为我们带来惊喜的结束。使用与上面相同的数据框,让我们尝试只计算列16777216.0的总和:

    float64

    突然间我们再次获得完全准确!发生什么了?这与dtypes几乎没有关系:我们仍在使用"A"进行求和。它现在是第二个步骤(NumPy求和),它负责区别。发生的事情是,NumPy可以(有时会)使用更准确的求和算法(称为pairwise summation)和>>> df[['A']].sum() A 100000000.0 dtype: float32 dtype以及我们正在使用的大小数组,这种准确性可以产生非常重要的意义与最终结果的差异。但是,它只在沿着阵列的变化最快的轴求和时才使用该算法;有关相关讨论,请参阅this NumPy issue。在我们计算列float32 float32的总和的情况下,我们最终会得到一个形状为"A"的值数组。变化最快的轴是轴1,我们计算沿轴0的和,所以使用了朴素求和算法,结果很差。但是如果我们只询问列"B"的总和,我们就会得到准确的求和结果,使用成对求和来计算。

    总而言之,当使用这个大小的DataFrame时,你要小心(a)尽可能使用双精度而不是单精度,并且(b)为NumPy做出不同的输出结果的差异做好准备算法选择。

答案 1 :(得分:1)

np.float32相对于np.float64

,您可能会失去精确度
np.finfo(np.float32)

finfo(resolution=1e-06, min=-3.4028235e+38, max=3.4028235e+38, dtype=float32)

np.finfo(np.float64)

finfo(resolution=1e-15, min=-1.7976931348623157e+308, max=1.7976931348623157e+308, dtype=float64)

一个人为的例子

df = pd.DataFrame(dict(
    x=[-60499999.315, 60500002.685] * int(2e7),
    y=[-60499999.315, 60500002.685] * int(2e7),
    z=[-60499999.315, 60500002.685] * int(2e7),
)).astype(dict(x=np.float64, y=np.float32, z=np.float32))

print(df.sum()[['y', 'z']], df[['y', 'z']].sum(), sep='\n\n')

y    80000000.0
z    80000000.0
dtype: float64

y    67108864.0
z    67108864.0
dtype: float32