Spark Scala中DataFrame列与RDD数组求和结果差异近三倍的问题
为什么两种求和方式结果差三倍?
哦这个坑我之前踩过!咱们来一步步拆解问题所在:
核心原因:数据类型转换导致的精度丢失/累加误差
你用df.rdd.map(x=>x(0).toString.toFloat).sum()的时候,做了两次没必要的转换:
- 把原始的age值(大概率是整数类型,比如Int/Long,或者高精度的Decimal)转成字符串
- 再把字符串转成Float类型
而Float的精度只有约7位有效数字,当处理1.4亿行的大数据量时,累加过程中会出现严重的精度丢失——尤其是当age值本身比较大的时候,单个值的近似误差会被不断放大,最终导致总和差出好几倍。
反观df.agg(sum("age")),Spark的内置sum函数会根据列的原始数据类型选择最优的累加逻辑:
- 如果是整数类型,用整数累加器,完全没有精度损失
- 如果是Decimal类型,会保留高精度计算
- 就算是浮点型,也会用更稳定的累加实现,比直接用RDD的Float sum靠谱得多
快速修复方案
试试下面几种方式,结果应该就能和df.agg(sum("age"))对齐了:
直接取原始数据类型,跳过多余转换
如果age列是整数类型(比如Long):df.rdd.map(_.getAs[Long]("age")).sum()如果是Decimal类型:
df.rdd.map(_.getAs[java.math.BigDecimal]("age")).reduce(_.add(_)).doubleValue()改用Double类型替代Float
要是必须用浮点型计算,Double的精度(15-17位有效数字)比Float高太多,误差会大幅降低:df.rdd.map(x=>x(0).toString.toDouble).sum()先确认原始数据类型
先跑个命令看看age列的真实类型,避免瞎猜:df.printSchema()比如输出如果是
|-- age: long (nullable = true),就用上面第一种Long的方式。
额外排查点
如果还是不对,检查下有没有脏数据:比如age列有没有null值?或者有没有非数值的字符串?不过Spark的sum函数会自动忽略null,但RDD的map如果遇到null转字符串再转Float可能会出问题(比如转成NaN,累加的时候NaN会导致结果异常),这时候可以加个过滤:
df.rdd.map(x=>Option(x(0)).map(_.toString.toDouble).getOrElse(0.0)).sum()
内容的提问来源于stack exchange,提问作者Muz
相关产品推荐
相关产品推荐

