为何Pandas中Sparse DataFrame调用sum()会返回错误结果?
Sparse DataFrame全局求和溢出问题及解决方案
问题描述
在Sparse DataFrame中,对整个数据集调用sum()方法会得到错误结果,但单独对某一列或数据集子集调用sum()却能正常工作。这明显是全局求和时的溢出问题——因为求和结果的类型被设置为Sparse[int8, 0],但为何另外两种场景不会出现该问题?
另外存在一个奇怪的环境差异:在Anaconda终端中运行时,所有场景均返回正确结果,但在PyCharm中会出现错误。
代码复现
import numpy as np import pandas as pd # 生成包含二进制变量的普通DataFrame和稀疏DataFrame # 使用int8以最小化内存占用 df = pd.DataFrame(np.random.randint(low=0, high=2, size=(50_000, 1))) sdf = df.astype(pd.SparseDtype(dtype='int8', fill_value=0)) print(df.sum(axis=0)) # 输出:0 24954 # dtype: int64 # 为什么这个会返回错误结果,另外两个却正常? print(sdf.sum(axis=0)) # 输出:0 122 # dtype: Sparse[int8, 0] # 正常工作 print(sdf[0].sum()) # 输出:24954 # 正常工作 print(sdf[sdf==1].sum()) # 输出:0 24954.0 # dtype: float64
原因分析
- 全局求和溢出:对整个Sparse DataFrame调用
sum(axis=0)时,Pandas默认会尝试保留原稀疏列的int8类型,但int8的取值范围仅为-128~127,而5万条数据的求和结果(约2.5万)远超出这个范围,触发整数溢出,最终得到的122是24954对256(2^8)取模后的结果。 - 单列求和正常:单独对某一列调用
sum()时,Pandas会自动将求和结果的类型提升为更大的整数类型(如int64),避免溢出,因此结果正确。 - 子集求和正常:
sdf[sdf==1]的筛选操作会将原int8稀疏列转换为浮点型稀疏列,求和时以浮点型计算,不会触发int8的溢出限制,因此结果正确。 - 环境差异原因:Anaconda终端和PyCharm中的Pandas/Numpy版本不同,旧版本的Pandas在Sparse DataFrame全局求和时未做类型提升处理,而新版本修复了该问题,导致不同环境下行为不一致。
安全求和方法(不转密集格式/修改dtype)
- 方法1:使用apply遍历列求和
直接对整个Sparse DataFrame调用apply,对每一列单独执行sum操作,写法简洁且结果正确:sdf.apply(lambda col: col.sum()) - 方法2:显式指定求和dtype
在sum()中通过dtype参数强制指定使用更大的整数类型(如int64)进行计算,避免溢出:sdf.sum(axis=0, dtype='int64')
内容的提问来源于stack exchange,提问作者Dudelstein
相关产品推荐
相关产品推荐

