以编程方式向Spark DataFrame添加多个列

时间:2015-09-15 07:41:44

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

我正在使用带有scala的spark。

我有一个包含3列的Dataframe:ID,Time,RawHexdata。 我有一个用户定义的函数,它接受RawHexData并将其扩展为X个更多列。重要的是要声明每行X是相同的(列不变)。但是,在收到第一个数据之前,我不知道列是什么。但是一旦我掌握了头脑,我就可以推断它了。

我想要第二个包含所述列的Dataframe:Id,Time,RawHexData,NewCol1,...,NewCol3。

我能想到的“最简单”方法是: 1.将每一行反序列化为json(每个数据都可以序列化) 2.添加我的新列, 3.从改变的json中反序列化一个新的数据帧,

然而,这似乎是浪费,因为它涉及2个昂贵且冗余的json序列化步骤。我正在寻找更清洁的模式。

使用case-classes,似乎是个坏主意,因为我事先不知道列数或列名。

2 个答案:

答案 0 :(得分:2)

动态扩展DataFrame可以做的是操作行RDD,您可以通过调用dataFrame.rdd来获取。拥有Row实例后,您可以访问RawHexdata列并解析包含的数据。通过将新解析的列添加到生成的Row,您几乎解决了问题。将RDD[Row]转换回DataFrame所需的唯一方法是生成新列的架构数据。您可以通过在驱动程序上收集单个RawHexdata值然后提取列类型来执行此操作。

以下代码说明了这种方法。

object App {

  case class Person(name: String, age: Int)

  def main(args: Array[String]) {
    val sparkConf = new SparkConf().setAppName("Test").setMaster("local[4]")
    val sc = new SparkContext(sparkConf)
    val sqlContext = new SQLContext(sc)
    import sqlContext.implicits._

    val input = sc.parallelize(Seq(Person("a", 1), Person("b", 2)))
    val dataFrame = input.df

    dataFrame.show()

    // create the extended rows RDD
    val rowRDD = dataFrame.rdd.map{
      row =>
        val blob = row(1).asInstanceOf[Int]
        val newColumns: Seq[Any] = Seq(blob, blob * 2, blob * 3)
        Row.fromSeq(row.toSeq.init ++ newColumns)
    }

    val schema = dataFrame.schema

    // we know that the new columns are all integers
    val newColumns = StructType{
      Seq(new StructField("1", IntegerType), new StructField("2", IntegerType), new StructField("3", IntegerType))
    }

    val newSchema = StructType(schema.init ++ newColumns)

    val newDataFrame = sqlContext.createDataFrame(rowRDD, newSchema)

    newDataFrame.show()
  }
}

答案 1 :(得分:0)

SELECT是您的朋友,无需返回RDD即可解决问题。

case class Entry(Id: String, Time: Long)

val entries = Seq(
  Entry("x1", 100L),
  Entry("x2", 200L)
)

val newColumns = Seq("NC1", "NC2", "NC3")

val df = spark.createDataFrame(entries)
  .select(col("*") +: (newColumns.map(c => lit(null).as(c))): _*)

df.show(false)

+---+----+----+----+----+
|Id |Time|NC1 |NC2 |NC3 |
+---+----+----+----+----+
|x1 |100 |null|null|null|
|x2 |200 |null|null|null|
+---+----+----+----+----+