簡體   English   中英

雙列數組動態轉換為嵌套火花中的多列 dataframe

[英]Dynamic conversion of Array of double columns into multiple columns in nested spark dataframe

我當前的 DataFrame 如下所示:

{"id":"1","inputs":{"values":{"0.2":[1,1],"0.4":[1,1],"0.6":[1,1]}},"id1":[1,2]}

我想將這個 dataframe 轉換成下面的 dataFrame:

{"id":"1", "v20":[1,1],"v40":[1,1],"v60":[1,1],"id1":[1,2]}

這意味着,每個“值”數組的項目(0.2、0.4 和 0.6)將乘以 100,以字母“v”作為前綴,並提取到單獨的列中。

為了實現這一點,代碼看起來如何。 我試過withColumn但無法實現。

試試下面的代碼,請找到代碼解釋的內聯注釋

import org.apache.spark.sql.SaveMode
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.StructType

object DynamicCol {

  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().master("local[*]").getOrCreate()
    val df = spark.read.json("src/main/resources/dyamicCol.json")    /// Load the JSON file
    val dfTemp = df.select(col("inputs.values").as("values")) // Temp Dataframe for fetching the nest values
    val index = dfTemp
      .schema.fieldIndex("values")
    val propSchema = dfTemp.schema(index).dataType.asInstanceOf[StructType]
    val dfFinal = propSchema.fields.foldLeft(df)( (df,field) => {     // Join Dataframe with the list of nested columns
      val colNameInt = (field.name.toDouble * 100).toInt
      val colName = s"v$colNameInt"
      df.withColumn(colName,col("inputs.values.`" + field.name + "`"))  // Add the nested column mappings
    }  ).drop("inputs") // Drop the extra column

    dfFinal.write.mode(SaveMode.Overwrite).json("src/main/resources/dyamicColOut.json") // Output the JSON file
  }

}

我會將更改列名拆分器的邏輯分為兩部分,一部分是數值,另一部分不變。

def stringDecimalToVNumber(colName:String): String =
  "v" + (colName.toFloat * 100).toInt.toString

並形成一個根據大小寫轉換的function

val floatRegex = """(\d+\.?\d*)""".r
def transformColumnName(colName:String): String = colName match {
  case floatRegex(v) => stringDecimalToVNumber(v) //it's a float, transform it
  case x => x // keep it

現在我們有 function 來轉換列的末尾,讓我們動態選擇模式。

val flattenDF = df.select("id","inputs.values.*")

val finalDF = flattenDF
  .schema.names
  .foldLeft(flattenDF)((dfacum,x) => {
    val newName = transformColumnName(x)
    if (newName == x)
      dfacum // the name didn't need to be changed
    else 
      dfacum.withColumnRenamed(x, transformColumnName(x))
  })

這會將 inputs.values 中的所有列動態轉換為新名稱,並將它們放在 id 旁邊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM