要了解这里发生了什么,您需要了解 Pandas 在幕后做了什么。我将稍微简化一下,因为有很多花哨的东西和特殊情况需要考虑,但大致如下:
假设你有一只熊猫DataFrame
object df
具有各种数字列(我们将忽略日期时间列、分类列等)。当你计算时df.sum()
,熊猫:
-
提取值 https://github.com/pandas-dev/pandas/blob/81372093f1fdc0c07e4b45ba0f47b0360fabd405/pandas/core/nanops.py#L308将数据帧转换为二维 NumPy 数组。
-
应用 NumPysum功能 https://github.com/pandas-dev/pandas/blob/81372093f1fdc0c07e4b45ba0f47b0360fabd405/pandas/core/nanops.py#L314到那个二维数组
axis=0
计算列总和。
这是这里很重要的第一步。 a 的列DataFrame
可能有不同的数据类型,但 2d NumPy 数组只能有一个数据类型。如果df
有一个混合物float32
and int32
列(例如),Pandas 必须选择一个同时适合两列的数据类型,在这种情况下,它选择float64
。因此,当计算总和时,会使用双精度算术根据双精度值进行计算。这就是您的第二个示例中发生的情况。
另一方面,如果你减少到只float32
首先,Pandas 可以并且将会使用列float32
2d NumPy 数组的 dtype,所以sum
计算以单精度执行。这就是您的第一个示例中发生的情况。
下面是一个简单的示例,展示了这一点:我们将设置一个包含 1 亿行和三列数据类型的 DataFramefloat32
, float32
and int32
分别。所有的值都是:
>>> 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
现在,当我们直接计算总和时,Pandas 首先将所有内容转换为float64
s。计算也使用以下方法完成float64
输入所有三列,我们就会得到准确的答案。
>>> df.sum()
A 100000000.0
B 100000000.0
C 100000000.0
dtype: float64
但是如果我们首先将数据框缩减为float32
列,然后float32
- 算术用于求和,我们得到的答案很差。
>>> df[['A', 'B']].sum()
A 16777216.0
B 16777216.0
dtype: float32
不准确当然是由于使用的数据类型对于所讨论的任务没有足够的精度:在求和的某个时刻,我们最终会重复添加1.0
to 16777216.0
,并得到16777216.0
每次回来,感谢常见的浮点问题 https://stackoverflow.com/q/12596695/270986。解决方案是显式转换为float64
在进行计算之前自己进行计算。
然而,Pandas 为我们带来的惊喜还没有结束。使用与上面相同的数据框,让我们尝试计算列的总和"A"
:
>>> df[['A']].sum()
A 100000000.0
dtype: float32
突然之间,我们又获得了完全的准确性!发生什么了?这与 dtypes 关系不大:我们仍在使用float32
进行求和。现在是second造成差异的步骤(NumPy 求和)。正在发生的事情是 NumPy 可以而且有时确实使用一种更准确的求和算法,称为两两求和 https://en.wikipedia.org/wiki/Pairwise_summation, 与float32
dtype 和我们使用的大小数组,其准确性会对最终结果产生巨大的影响。但是,它仅在沿数组变化最快的轴求和时才使用该算法;看这个 NumPy 问题 https://github.com/numpy/numpy/issues/9393进行相关讨论。在我们计算两列总和的情况下"A"
and column "B"
,我们最终得到一个形状的值数组(100000000, 2)
。变化最快的轴是轴 1,而我们沿轴 0 计算总和,因此使用了朴素求和算法,但得到的结果很差。但如果我们只要求列的总和"A"
,我们得到了使用成对求和计算的准确求和结果。
总之,在使用这种大小的 DataFrame 时,您需要小心(a)尽可能使用双精度而不是单精度,以及(b)准备好应对由于 NumPy 做出不同算法选择而导致的输出结果差异。