如何将DataFrame中的struct映射到case类?

时间:2017-04-08 14:38:22

标签: scala apache-spark dataframe apache-spark-sql apache-spark-2.0

在我的应用程序中的某个时刻,我有一个DataFrame,其中包含从案例类创建的Struct字段。现在我想将它转换/映射回case类类型:

<form id="myForm">
   <input type="email" required />
   <button>Send</button>
</form>

<div id="message"></div>

基本:

import spark.implicits._
case class Location(lat: Double, lon: Double)

scala> Seq((10, Location(35, 25)), (20, Location(45, 35))).toDF
res25: org.apache.spark.sql.DataFrame = [_1: int, _2: struct<lat: double, lon: double>]

scala> res25.printSchema
root
 |-- _1: integer (nullable = false)
 |-- _2: struct (nullable = true)
 |    |-- lat: double (nullable = false)
 |    |-- lon: double (nullable = false)

看起来很脏 有没有更简单的方法?

3 个答案:

答案 0 :(得分:3)

在Spark 1.6+中,如果要保留保留的类型信息,请使用数据集(DS),而不是DataFrame(DF)。

import spark.implicits._
case class Location(lat: Double, lon: Double)

scala> Seq((10, Location(35, 25)), (20, Location(45, 35))).toDS
res25: org.apache.spark.sql.Dataset[(Int, Location)] = [_1: int, _2: struct<lat: double, lon: double>]

scala> res25.printSchema
root
 |-- _1: integer (nullable = false)
 |-- _2: struct (nullable = true)
 |    |-- lat: double (nullable = false)
 |    |-- lon: double (nullable = false)

它会给你Dataset[(Int, Location)]。现在,如果你想再次回到它的案例类原点,那么就这样做:

scala> res25.map(r => r._2).show(1)
+----+----+
| lat| lon|
+----+----+
|35.0|25.0|
+----+----+

但是,如果你想坚持DataFrame API,因为它是动态类型的本质,那么你必须像这样编码:

scala> res25.select("_2.*").map(r => Location(r.getDouble(0), r.getDouble(1))).show(1)
+----+----+
| lat| lon|
+----+----+
|35.0|25.0|
+----+----+

答案 1 :(得分:2)

你也可以使用Row中的提取器模式,使用更多惯用的scala来提供类似的结果:

scala> res25.map { row =>
  (row: @unchecked) match {
    case Row(a: Int, Row(b: Double, c: Double)) => (a, Location(b, c))
  }
}
res26: org.apache.spark.sql.Dataset[(Int, Location)] = [_1: int, _2: struct<lat: double, lon: double>]
scala> res26.collect()
res27: Array[(Int, Location)] = Array((10,Location(35.0,25.0)), (20,Location(45.0,35.0)))

答案 2 :(得分:2)

我认为其他答案已经确定了,但也许他们可能还需要其他一些措辞。

简而言之,不可能在DataFrame中使用案例类,因为他们不了解案例类并使用RowEncoder将内部SQL类型映射到Row

正如其他答案所述,您必须使用Row运营商将基于DataFrame的{​​{1}}转变为Dataset

as