Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Json Spark Scala-将结构数组拆分为数据帧列_Json_Scala_Apache Spark - Fatal编程技术网

Json Spark Scala-将结构数组拆分为数据帧列

Json Spark Scala-将结构数组拆分为数据帧列,json,scala,apache-spark,Json,Scala,Apache Spark,我有一个嵌套的源json文件,其中包含一个结构数组。结构的数量因行而异,我想使用Spark(scala)从结构的键/值动态创建新的dataframe列,其中键是列名,值是列值 简化json记录示例 数据帧模式 到目前为止做了什么 这里有一个由3个结构组成的数组,但是3个结构需要动态地拆分成3个独立的列(3个的数量可能会有很大的变化),我不知道如何做到这一点 采样期望输出 请注意,values数组中的每个数组元素都生成了3个新列 +----+----+----+----+-------------

我有一个嵌套的源json文件,其中包含一个结构数组。结构的数量因行而异,我想使用Spark(scala)从结构的键/值动态创建新的dataframe列,其中键是列名,值是列值

简化json记录示例 数据帧模式 到目前为止做了什么 这里有一个由3个结构组成的数组,但是3个结构需要动态地拆分成3个独立的列(3个的数量可能会有很大的变化),我不知道如何做到这一点

采样期望输出 请注意,
values
数组中的每个数组元素都生成了3个新列

+----+----+----+----+-----------------------------------------+
|key3|key4|key6|key7|valuesColumn1|valuesColumn2|valuesColumn3|
+----+----+----+----+-----------------------------------------+
|AK  |EU  |001 |N   |9.876        |1.2345        |8.675309    |
+----+----+----+----+-----------------------------------------+
参考文献 我认为理想的解决方案只有两个主要区别:

  • 在SO post中,列数硬编码为3,但在我的情况下,数组元素数未知
  • 列名需要由
    名称
    列驱动,列值需要由
    驱动

  • 你可以这样做:

    val sac = new SparkContext("local[*]", " first Program");
    val sqlc = new SQLContext(sac);
    import sqlc.implicits._;
    import org.apache.spark.sql.functions.split
    import scala.math._
    import org.apache.spark.sql.types._
    import org.apache.spark.sql.functions._
    import org.apache.spark.sql.functions.{ min, max }
    
    val json = """{"key1":{"key2":{"key3":"AK","key4":"EU","key5":{"key6":"001","key7":"N","values":[{"name":"valuesColumn1","value":"9.876"},{"name":"valuesColumn2","value":"1.2345"},{"name":"valuesColumn3","value":"8.675309"}]}}}}"""
    
    val df1 = sqlc.read.json(Seq(json).toDS())
    
    val df2 = df1.select(
        ($"key1.key2.key3").as("key3"),
        ($"key1.key2.key4").as("key4"),
        ($"key1.key2.key5.key6").as("key6"),
        ($"key1.key2.key5.key7").as("key7"),
        ($"key1.key2.key5.values").as("values")
    )
    
    val numColsVal = df2
        .withColumn("values_size", size($"values"))
        .agg(max($"values_size"))
        .head()
        .getInt(0)
    
    val finalDFColumns = df2.select(explode($"values").as("values")).select("values.*").select("name").distinct.map(_.getAs[String](0)).orderBy($"value".asc).collect.foldLeft(df2.limit(0))((cdf, c) => cdf.withColumn(c, lit(null))).columns
    val finalDF = df2.select($"*" +: (0 until numColsVal).map(i => $"values".getItem(i)("value").as($"values".getItem(i)("name").toString)): _*)
    finalDF.columns.zip(finalDFColumns).foldLeft(finalDF)((fdf, column) => fdf.withColumnRenamed(column._1, column._2)).show(false)
    finalDF.columns.zip(finalDFColumns).foldLeft(finalDF)((fdf, column) => fdf.withColumnRenamed(column._1, column._2)).drop($"values").show(false)
    
    最终结果如下:

    +----+----+----+----+-------------+-------------+-------------+
    |key3|key4|key6|key7|valuesColumn1|valuesColumn2|valuesColumn3|
    +----+----+----+----+-------------+-------------+-------------+
    |AK  |EU  |001 |N   |9.876        |1.2345       |8.675309     |
    +----+----+----+----+-------------+-------------+-------------+
    
    希望我没有弄错你的问题

    -----------编辑并解释----------

    此块获取要为数组结构创建的列数

    val numColsVal = df2
            .withColumn("values_size", size($"values"))
            .agg(max($"values_size"))
            .head()
            .getInt(0)
    
    df2.select(explode($"values").as("values")).select("values.*").select("name").distinct.map(_.getAs[String](0)).orderBy($"value".asc).collect
    
    finalDFColumns
    是创建的DF,所有预期列都作为空值的输出

    foldLeft(df2.limit(0))((cdf, c) => cdf.withColumn(c, lit(null)))
    
    块返回需要从数组结构创建的不同列

    val numColsVal = df2
            .withColumn("values_size", size($"values"))
            .agg(max($"values_size"))
            .head()
            .getInt(0)
    
    df2.select(explode($"values").as("values")).select("values.*").select("name").distinct.map(_.getAs[String](0)).orderBy($"value".asc).collect
    
    下面的块将上述新列与用空/空值初始化的
    df2
    中的其他列组合在一起

    foldLeft(df2.limit(0))((cdf, c) => cdf.withColumn(c, lit(null)))
    
    如果打印输出,将这两个块组合在一起,您将得到:

    +----+----+----+----+------+-------------+-------------+-------------+
    |key3|key4|key6|key7|values|valuesColumn1|valuesColumn2|valuesColumn3|
    +----+----+----+----+------+-------------+-------------+-------------+
    +----+----+----+----+------+-------------+-------------+-------------+
    
    现在我们已经准备好了结构。我们需要相应列的值。下面的块为我们提供了值:

    df2.select($"*" +: (0 until numColsVal).map(i => $"values".getItem(i)("value").as($"values".getItem(i)("name").toString)): _*)
    
    结果如下:

    +----+----+----+----+--------------------+---------------+---------------+---------------+
    |key3|key4|key6|key7|              values|values[0][name]|values[1][name]|values[2][name]|
    +----+----+----+----+--------------------+---------------+---------------+---------------+
    |  AK|  EU| 001|   N|[[valuesColumn1, ...|          9.876|         1.2345|       8.675309|
    +----+----+----+----+--------------------+---------------+---------------+---------------+
    
    
    现在我们需要像上面第一个块中那样重命名这些列。因此,我们将使用
    zip
    函数合并列,然后使用foldLeft方法重命名输出列,如下所示:

    finalDF.columns.zip(finalDFColumns).foldLeft(finalDF)((fdf, column) => fdf.withColumnRenamed(column._1, column._2)).show(false)
    
    这将导致以下结构:

    +----+----+----+----+--------------------+-------------+-------------+-------------+
    |key3|key4|key6|key7|              values|valuesColumn1|valuesColumn2|valuesColumn3|
    +----+----+----+----+--------------------+-------------+-------------+-------------+
    |  AK|  EU| 001|   N|[[valuesColumn1, ...|        9.876|       1.2345|     8.675309|
    +----+----+----+----+--------------------+-------------+-------------+-------------+
    
    
    我们快到了。我们现在只需要删除不需要的
    列,如下所示:

    finalDF.columns.zip(finalDFColumns).foldLeft(finalDF)((fdf, column) => fdf.withColumnRenamed(column._1, column._2)).drop($"values").show(false)
    
    从而产生如下预期产出-

    +----+----+----+----+-------------+-------------+-------------+
    |key3|key4|key6|key7|valuesColumn1|valuesColumn2|valuesColumn3|
    +----+----+----+----+-------------+-------------+-------------+
    |AK  |EU  |001 |N   |9.876        |1.2345       |8.675309     |
    +----+----+----+----+-------------+-------------+-------------+
    

    我不确定我是否能解释清楚。但是,如果您尝试打破上述语句/代码,并尝试打印它,您将了解我们是如何达到输出的。您可以在internet上找到此逻辑中使用的不同函数的示例说明。

    我发现此方法性能更好,使用分解和透视更容易理解:

    val json = """{"key1":{"key2":{"key3":"AK","key4":"EU","key5":{"key6":"001","key7":"N","values":[{"name":"valuesColumn1","value":"9.876"},{"name":"valuesColumn2","value":"1.2345"},{"name":"valuesColumn3","value":"8.675309"}]}}}}"""
    
    val df = spark.read.json(Seq(json).toDS())
    
    // schema
    df.printSchema
    root
     |-- key1: struct (nullable = true)
     |    |-- key2: struct (nullable = true)
     |    |    |-- key3: string (nullable = true)
     |    |    |-- key4: string (nullable = true)
     |    |    |-- key5: struct (nullable = true)
     |    |    |    |-- key6: string (nullable = true)
     |    |    |    |-- key7: string (nullable = true)
     |    |    |    |-- values: array (nullable = true)
     |    |    |    |    |-- element: struct (containsNull = true)
     |    |    |    |    |    |-- name: string (nullable = true)
     |    |    |    |    |    |-- value: string (nullable = true)
    
    // create final df
    val finalDf = df.
        select(
          $"key1.key2.key3".as("key3"),
          $"key1.key2.key4".as("key4"),
          $"key1.key2.key5.key6".as("key6"),
          $"key1.key2.key5.key7".as("key7"),
          explode($"key1.key2.key5.values").as("values")
        ).
        groupBy(
          $"key3", $"key4", $"key6", $"key7"
        ).
        pivot("values.name").
        agg(min("values.value")).alias("values.name")
    
    // result
    finalDf.show
    +----+----+----+----+-------------+-------------+-------------+
    |key3|key4|key6|key7|valuesColumn1|valuesColumn2|valuesColumn3|
    +----+----+----+----+-------------+-------------+-------------+
    |  AK|  EU| 001|   N|        9.876|       1.2345|     8.675309|
    +----+----+----+----+-------------+-------------+-------------+
    

    感谢您的回复,这看起来确实解决了问题,但是您能否提供这里发生的事情的一些细节?我试图解释用于获取输出的逻辑部分。不确定我是否解释得很好:)编辑了与解释相同的答案。如果你觉得有帮助,请将其标记为答案。
    val json = """{"key1":{"key2":{"key3":"AK","key4":"EU","key5":{"key6":"001","key7":"N","values":[{"name":"valuesColumn1","value":"9.876"},{"name":"valuesColumn2","value":"1.2345"},{"name":"valuesColumn3","value":"8.675309"}]}}}}"""
    
    val df = spark.read.json(Seq(json).toDS())
    
    // schema
    df.printSchema
    root
     |-- key1: struct (nullable = true)
     |    |-- key2: struct (nullable = true)
     |    |    |-- key3: string (nullable = true)
     |    |    |-- key4: string (nullable = true)
     |    |    |-- key5: struct (nullable = true)
     |    |    |    |-- key6: string (nullable = true)
     |    |    |    |-- key7: string (nullable = true)
     |    |    |    |-- values: array (nullable = true)
     |    |    |    |    |-- element: struct (containsNull = true)
     |    |    |    |    |    |-- name: string (nullable = true)
     |    |    |    |    |    |-- value: string (nullable = true)
    
    // create final df
    val finalDf = df.
        select(
          $"key1.key2.key3".as("key3"),
          $"key1.key2.key4".as("key4"),
          $"key1.key2.key5.key6".as("key6"),
          $"key1.key2.key5.key7".as("key7"),
          explode($"key1.key2.key5.values").as("values")
        ).
        groupBy(
          $"key3", $"key4", $"key6", $"key7"
        ).
        pivot("values.name").
        agg(min("values.value")).alias("values.name")
    
    // result
    finalDf.show
    +----+----+----+----+-------------+-------------+-------------+
    |key3|key4|key6|key7|valuesColumn1|valuesColumn2|valuesColumn3|
    +----+----+----+----+-------------+-------------+-------------+
    |  AK|  EU| 001|   N|        9.876|       1.2345|     8.675309|
    +----+----+----+----+-------------+-------------+-------------+