Scala Spark 在数据帧和数据集中以不同方式处理 Double.NaN
Posted
技术标签:
【中文标题】Scala Spark 在数据帧和数据集中以不同方式处理 Double.NaN【英文标题】:Scala Spark handles Double.NaN differently in dataframe and dataset 【发布时间】:2021-01-01 23:12:09 【问题描述】:在测试中,我正在尝试将数据帧/数据集转换为集合并进行比较。例如
actualResult.collect.toSet should be(expectedResult.collect.toSet)
我注意到一些关于 Double.NaN
价值的事实。
-
在 Scala 中,
Double.NaN == Double.NaN
返回 false。
在火花中NaN == NaN
是真的。 (offical doc)
但我无法弄清楚为什么数据框和数据集的行为不同。
import org.apache.spark.sql.SparkSession
object Main extends App
val spark = SparkSession.builder().appName("Example").master("local").getOrCreate()
import spark.implicits._
val dataSet = spark.createDataset(Seq(Book("book 1", Double.NaN)))
// Compare Set(Book(book 1,NaN)) to itself
println(dataSet.collect.toSet == dataSet.collect.toSet) //false, why?
// Compare Set([book 1,NaN]) to itself
println(dataSet.toDF().collect.toSet == dataSet.toDF().collect.toSet) //true, why?
case class Book (title: String, price: Double)
这是我的问题。欣赏任何见解。
-
它在代码中是如何发生的? (
equals
在哪里被覆盖?等等)
这种设计背后有什么原因吗?是否有更好的范例来在测试中断言数据集/数据框?
【问题讨论】:
【参考方案1】:关于这个话题,我有几点想分享。
-
当您执行
dataSet.collect.toSet
时,您将其收集为 Set[Book],并且当您在两组图书对象之间进行比较时。
单个(书)对象相等方法用于您在Book Case类中定义的比较。
这就是为什么 println(dataSet.collect.toSet == dataSet.collect.toSet)
返回 false 的原因,因为 Double.NaN == Double.NaN returns false
。
-
当您执行
dataSet.toDF().collect.toSet
时,您将其收集为Set[Row]
当您执行 toDF 时,spark 将转换**(即序列化 Book 然后反序列化为 javaType 字段 Row)** Book 类为 Row在此过程中,它还使用RowEncoders 对字段进行一些转换。
使用 RowEncoder.scala 中的以下代码将所有 Primitive 字段转换为 java 类型
def apply(schema: StructType): ExpressionEncoder[Row] =
val cls = classOf[Row]
**val inputObject = BoundReference(0, ObjectType(cls), nullable = true)
val serializer = serializerFor(AssertNotNull(inputObject, Seq("top level row object")), schema)
val deserializer = deserializerFor(schema)**
new ExpressionEncoder[Row](
schema,
flat = false,
serializer.asInstanceOf[CreateNamedStruct].flatten,
deserializer,
ClassTag(cls))
如果你查看Double.java和Float.java相等方法的源代码。 NAN 的比较将返回 true。这就是 Row 对象比较将返回 true 的原因。 println(dataSet.toDF().collect.toSet == dataSet.toDF().collect.toSet)
是真的。
<li>If @code d1 and @code d2 both represent
* @code Double.NaN, then the @code equals method
* returns @code true, even though
* @code Double.NaN==Double.NaN has the value
* @code false.
* <li>If @code d1 represents @code +0.0 while
* @code d2 represents @code -0.0, or vice versa,
* the @code equal test has the value @code false,
* even though @code +0.0==-0.0 has the value @code true.
* </ul>
**对不起,如果我语法错误。
【讨论】:
以上是关于Scala Spark 在数据帧和数据集中以不同方式处理 Double.NaN的主要内容,如果未能解决你的问题,请参考以下文章
Spark MultilayerPerceptronClassifier Scala实现例子及优化算法理解