如何将RDD [Row]转换回DataFrame

时间:2016-05-03 18:03:05

标签: scala apache-spark dataframe rdd

我一直在玩转换RDD到DataFrames然后再回来。首先,我有一个名为dataPair的类型(Int,Int)的RDD。然后我使用:

创建了一个带有列标题的DataFrame对象
val dataFrame = dataPair.toDF(header(0), header(1))

然后我使用:

将它从DataFrame转换回RDD
val testRDD = dataFrame.rdd

返回类型为org.apache.spark.sql.Row的RDD(不是(Int,Int))。然后我想使用.toDF将其转换回RDD,但是我收到错误:

error: value toDF is not a member of org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]

我已经尝试为testRDD定义Data(Int,Int)类型的Schema,但是我得到了类型不匹配的异常:

error: type mismatch;
found   : org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]
required: org.apache.spark.rdd.RDD[Data]
    val testRDD: RDD[Data] = dataFrame.rdd
                                       ^

我已经导入了

import sqlContext.implicits._

1 个答案:

答案 0 :(得分:21)

要从RDD of Rows创建DataFrame,通常有两个主要选项:

1)您可以使用可由toDF()导入的import sqlContext.implicits._。但是,此方法仅适用于以下类型的RDD:

  • RDD[Int]
  • RDD[Long]
  • RDD[String]
  • RDD[T <: scala.Product]

(来源:SQLContext.implicits对象的Scaladoc

最后一个签名实际上意味着它可以用于元组的RDD或案例类的RDD(因为元组和case类是scala.Product的子类)。

因此,要将此方法用于RDD[Row],您必须将其映射到RDD[T <: scala.Product]。这可以通过将每一行映射到自定义案例类或元组来完成,如以下代码片段所示:

val df = rdd.map({ 
  case Row(val1: String, ..., valN: Long) => (val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")

case class MyClass(val1: String, ..., valN: Long = 0L)
val df = rdd.map({ 
  case Row(val1: String, ..., valN: Long) => MyClass(val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")

这种方法的主要缺点(在我看来)是你必须逐列显式地设置map函数中结果DataFrame的模式。如果你事先不知道架构,也许这可以以编程方式完成,但事情可能会有些混乱。所以,或者,还有另一种选择:

2)您可以使用SQLContext对象中提供的createDataFrame(rowRDD: RDD[Row], schema: StructType)。例如:

val df = oldDF.sqlContext.createDataFrame(rdd, oldDF.schema)

请注意,无需显式设置任何架构列。我们重用旧的DF模式,它是StructType类,可以很容易地扩展。但是,这种方法有时是不可能的,在某些情况下效率可能低于第一种方法。

我希望它比以前更清楚。欢呼声。