我的数据框包含float
和double
值。
scala> val df = List((Float.NaN, Double.NaN), (1f, 0d)).toDF("x", "y")
df: org.apache.spark.sql.DataFrame = [x: float, y: double]
scala> df.show
+---+---+
| x| y|
+---+---+
|NaN|NaN|
|1.0|0.0|
+---+---+
scala> df.printSchema
root
|-- x: float (nullable = false)
|-- y: double (nullable = false)
当我用NaN
值替换null
值时,我在null
操作中将fill
作为字符串给了地图。
scala> val map = df.columns.map((_, "null")).toMap
map: scala.collection.immutable.Map[String,String] = Map(x -> null, y -> null)
scala> df.na.fill(map).printSchema
root
|-- x: float (nullable = true)
|-- y: double (nullable = true)
scala> df.na.fill(map).show
+----+----+
| x| y|
+----+----+
|null|null|
| 1.0| 0.0|
+----+----+
我得到了正确的价值。但是我无法理解Spark SQL如何/为什么将null
作为字符串转换为null
对象?
答案 0 :(得分:3)
如果您查看fill
中的Dataset
函数,它会检查数据类型并尝试转换为其列模式的数据类型。如果它可以被转换,那么它将转换,否则返回null。
它不会转换为" null
"对象null
但如果转换时发生异常则返回null。
val map = df.columns.map((_, "WHATEVER")).toMap
给出
and val map = df.columns.map((_, "9999.99")).toMap
给出9999.99
如果要更新具有相同数据类型的NAN
,可以按预期获得结果。
希望这有助于您理解!
答案 1 :(得分:1)
不是" null"作为String转换为null
对象。您可以尝试使用任何String的转换并仍然获得null
(除了可以直接转换为double / float的字符串,见下文)。例如,使用
val map = df.columns.map((_, "abc")).toMap
会给出相同的结果。我的猜测是,由于列的类型为float
而double
将NaN
值转换为字符串,因此会null
。使用数字可以按预期工作,例如
val map = df.columns.map((_, 1)).toMap
由于某些字符串可以直接转换为double
或float
,因此在这种情况下也可以使用这些字符串。
val map = df.columns.map((_, "1")).toMap
答案 2 :(得分:1)
我已查看了源代码,在fill
中,您的字符串被转换为double / float:
private def fillCol[T](col: StructField, replacement: T): Column = {
col.dataType match {
case DoubleType | FloatType =>
coalesce(nanvl(df.col("`" + col.name + "`"), lit(null)),
lit(replacement).cast(col.dataType)).as(col.name)
case _ =>
coalesce(df.col("`" + col.name + "`"), lit(replacement).cast(col.dataType)).as(col.name)
}
}
此处的相关源代码(Floats代码类似):
Cast.scala(取自Spark 1.6.3):
// DoubleConverter
private[this] def castToDouble(from: DataType): Any => Any = from match {
case StringType =>
buildCast[UTF8String](_, s => try s.toString.toDouble catch {
case _: NumberFormatException => null
})
case BooleanType =>
buildCast[Boolean](_, b => if (b) 1d else 0d)
case DateType =>
buildCast[Int](_, d => null)
case TimestampType =>
buildCast[Long](_, t => timestampToDouble(t))
case x: NumericType =>
b => x.numeric.asInstanceOf[Numeric[Any]].toDouble(b)
}
所以Spark尝试将String
转换为Double
(s.toString.toDouble
),如果那是不可能的(即你获得NumberFormatException
),你得到{{1 }}。因此,您可以使用null
而不是"null"
而不是"foo"
。但是,如果您在地图中使用"1.0"
,则NaNs
和nulls
将替换为1.0
,因为String
"1.0"
确实可解析为Double
。