scala spark在dataframe和dataset中处理double.nan的方式不同

9ceoxa92  于 2021-05-27  发布在  Spark
关注(0)|答案(1)|浏览(535)

在测试中,我尝试将Dataframe/数据集转换为集合并进行比较。例如

  1. actualResult.collect.toSet should be(expectedResult.collect.toSet)

我注意到一些关于 Double.NaN 价值观。
在斯卡拉, Double.NaN == Double.NaN 返回false。
Spark NaN == NaN 这是真的(官方文件)
但我不明白为什么dataframe和dataset的行为不同。

  1. import org.apache.spark.sql.SparkSession
  2. object Main extends App {
  3. val spark = SparkSession.builder().appName("Example").master("local").getOrCreate()
  4. import spark.implicits._
  5. val dataSet = spark.createDataset(Seq(Book("book 1", Double.NaN)))
  6. // Compare Set(Book(book 1,NaN)) to itself
  7. println(dataSet.collect.toSet == dataSet.collect.toSet) //false, why?
  8. // Compare Set([book 1,NaN]) to itself
  9. println(dataSet.toDF().collect.toSet == dataSet.toDF().collect.toSet) //true, why?
  10. }
  11. case class Book (title: String, price: Double)

这是我的问题。感谢你的真知灼见。
它是如何在代码中发生的(在哪里 equals 是否被覆盖?等等…)
这种设计背后有什么原因吗?有没有更好的范例在测试中Assert数据集/Dataframe?

p8ekf7hl

p8ekf7hl1#

我有几点想和大家分享。
当你这么做的时候 dataSet.collect.toSet 当你在两组图书对象之间进行比较时,你把它收集为set[book]。
单个(book)objects equal方法用于在book case类中定义的比较。这就是为什么 println(dataSet.collect.toSet == dataSet.collect.toSet) 返回false是因为 Double.NaN == Double.NaN returns false .
当你这么做的时候 dataSet.toDF().collect.toSet 按集合[行]收集
当您执行todf时,spark将转换**(即序列化book,然后反序列化为javatype fields row)**book类到row在这个过程中,它还使用行编码器对字段进行一些转换。
使用rowcoder.scala中的以下代码将所有原语字段转换为java类型

  1. def apply(schema: StructType): ExpressionEncoder[Row] = {
  2. val cls = classOf[Row]
  3. **val inputObject = BoundReference(0, ObjectType(cls), nullable = true)
  4. val serializer = serializerFor(AssertNotNull(inputObject, Seq("top level row object")), schema)
  5. val deserializer = deserializerFor(schema)**
  6. new ExpressionEncoder[Row](
  7. schema,
  8. flat = false,
  9. serializer.asInstanceOf[CreateNamedStruct].flatten,
  10. deserializer,
  11. ClassTag(cls))
  12. }

如果您检查double.java和float.java equal方法的源代码。比较一下楠就会变成真的。这就是为什么行对象比较将返回true。以及 println(dataSet.toDF().collect.toSet == dataSet.toDF().collect.toSet) 这是真的。

  1. <li>If {@code d1} and {@code d2} both represent
  2. * {@code Double.NaN}, then the {@code equals} method
  3. * returns {@code true}, even though
  4. * {@code Double.NaN==Double.NaN} has the value
  5. * {@code false}.
  6. * <li>If {@code d1} represents {@code +0.0} while
  7. * {@code d2} represents {@code -0.0}, or vice versa,
  8. * the {@code equal} test has the value {@code false},
  9. * even though {@code +0.0==-0.0} has the value {@code true}.
  10. * </ul>

**抱歉,如果我的语法错了。

展开查看全部

相关问题