Scala 如何';展平';具有可变列数的Spark架构?

Scala 如何';展平';具有可变列数的Spark架构?,scala,apache-spark,Scala,Apache Spark,这是我创建的Spark数据帧的模式: root |-- id: double (nullable = true) |-- sim_scores: struct (nullable = true) | |-- scores: map (nullable = true) | | |-- key: string | | |-- value: map (valueContainsNull = true) | | | |-- key: integ

这是我创建的Spark数据帧的模式:

root
 |-- id: double (nullable = true)
 |-- sim_scores: struct (nullable = true)
 |    |-- scores: map (nullable = true)
 |    |    |-- key: string
 |    |    |-- value: map (valueContainsNull = true)
 |    |    |    |-- key: integer
 |    |    |    |-- value: vector (valueContainsNull = true)
“sim_scores”结构表示用于聚合目的的Scala案例类。我定制了一个UDAF,用于合并这些结构。为了使它们在所有边缘情况下都能安全地合并,它们看起来就像这样。让我们假设这个问题,他们必须保持这种状态

我想将此数据帧“展平”为:

root
 |-- id: double (nullable = true)
 |-- score_1: map (valueContainsNull = true)
 |    |-- key: integer
 |    |-- value: vector (valueContainsNull = true)
 |-- score_2: map (valueContainsNull = true)
 |    |-- key: integer
 |    |-- value: vector (valueContainsNull = true)
 |-- score_3: map (valueContainsNull = true)
 |    |-- key: integer
 |    |-- value: vector (valueContainsNull = true)
...
“分数”结构中的外部映射类型将分数主题映射到文档;表示文档的内部映射将文档中的句子位置映射到向量分数。“分数1”、“分数2”和。。。表示初始DF中“分数”映射类型的所有可能键

用json的术语来说,如果我有一个如下的输入:

{ "id": 739874.0,
  "sim_scores": {
    "firstTopicName": {
      1: [1,9,1,0,1,1,4,6],
      2: [5,7,8,2,4,3,1,3],
      ...
    },
    "anotherTopic": {
      1: [6,8,4,1,3,4,2,0],
      2: [0,1,3,2,4,5,6,2],
      ...
    }
  }
}
然后我会得到一个输出

{ "id": 739874.0,
  "firstTopicName": {
    1: [1,9,1,0,1,1,4,6],
    2: [5,7,8,2,4,3,1,3],
    ...
  }
  "anotherTopic": {
    1: [6,8,4,1,3,4,2,0],
    2: [0,1,3,2,4,5,6,2],
    ...
  }
}
如果我知道主题栏的总数,这将很容易;但我没有。主题的数量由用户在运行时设置,输出数据帧的列数可变。它保证>=1,但我需要设计它,以便在必要时可以处理100个不同的主题列

我如何实现这一点


最后一点:我一直在使用Spark 1.6.3;因此,使用该版本的解决方案是最好的。但是,我会采取任何方式来实现它,希望将来能够实现。

从高层次上讲,我认为您有两种选择:

  • 使用DataFrameAPI
  • 切换到RDD
  • 如果要继续使用spark SQL,则可以使用
    selectExpr
    并生成select查询:

    it("should flatten using dataframes and spark sql") {
      val sqlContext = new SQLContext(sc)
      val df = sqlContext.createDataFrame(sc.parallelize(rows), schema)
      df.printSchema()
      df.show()
      val numTopics = 3 // input from user
      // fancy logic to generate the select expression
      val selectColumns: Seq[String] = "id" +: 1.to(numTopics).map(i => s"sim_scores['scores']['topic${i}']")
      val df2 = df.selectExpr(selectColumns:_*)
      df2.printSchema()
      df2.show()
    }
    
    鉴于此示例数据:

    val schema = sql.types.StructType(List(
      sql.types.StructField("id", sql.types.DoubleType, nullable = true),
      sql.types.StructField("sim_scores", sql.types.StructType(List(
        sql.types.StructField("scores", sql.types.MapType(sql.types.StringType, sql.types.MapType(sql.types.IntegerType, sql.types.StringType)), nullable = true)
      )), nullable = true)
    ))
    val rows = Seq(
      sql.Row(1d, sql.Row(Map("topic1" -> Map(1 -> "scores1"), "topic2" -> Map(1 -> "scores2")))),
      sql.Row(2d, sql.Row(Map("topic1" -> Map(1 -> "scores1"), "topic2" -> Map(1 -> "scores2")))),
      sql.Row(3d, sql.Row(Map("topic1" -> Map(1 -> "scores1"), "topic2" -> Map(1 -> "scores2"), "topic3" -> Map(1 -> "scores3"))))
    )
    
    您将得到以下结果:

    root
     |-- id: double (nullable = true)
     |-- sim_scores.scores[topic1]: map (nullable = true)
     |    |-- key: integer
     |    |-- value: string (valueContainsNull = true)
     |-- sim_scores.scores[topic2]: map (nullable = true)
     |    |-- key: integer
     |    |-- value: string (valueContainsNull = true)
     |-- sim_scores.scores[topic3]: map (nullable = true)
     |    |-- key: integer
     |    |-- value: string (valueContainsNull = true)
    
    +---+-------------------------+-------------------------+-------------------------+
    | id|sim_scores.scores[topic1]|sim_scores.scores[topic2]|sim_scores.scores[topic3]|
    +---+-------------------------+-------------------------+-------------------------+
    |1.0|        Map(1 -> scores1)|        Map(1 -> scores2)|                     null|
    |2.0|        Map(1 -> scores1)|        Map(1 -> scores2)|                     null|
    |3.0|        Map(1 -> scores1)|        Map(1 -> scores2)|        Map(1 -> scores3)|
    +---+-------------------------+-------------------------+-------------------------+
    
    另一种选择是切换到处理RDD,在RDD中可以根据地图中的键添加更强大的平坦化逻辑

    可能重复的