我有一个带有一个struct type列的Dataframe。示例数据框架构为:
root
|-- Data: array (nullable = true)
| |-- element: struct (containsNull = true)
| | |-- name: string (nullable = true)
| | |-- value: string (nullable = true)
字段name
保存列名,字段value
保存列值。 Data
列中的元素数量未定义,因此可能会有所不同。我需要解析该数据并摆脱嵌套结构。 (数组Explode
在这种情况下将不起作用,因为一行中的数据属于一个元素)。实际的模式要大得多,并且具有多个类似“数据”的数组字段,因此我的目标是创建一个通用的解决方案,该解决方案将应用于相似的结构数组。
示例:
样本数据:
val data = Seq(
"""{"Data": [{ "name": "FName", "value": "Alex" }, { "name": "LName", "value": "Strong" }]}""",
"""{"Data": [{ "name": "FName", "value": "Robert " }, { "name": "MName", "value": "Nesta " }]} { "name": "LName", "value": "Marley" }]}"""
)
val df = spark.read.json(spark.sparkContext.parallelize(data))
预期结果:
+-------+------+
| FName| LName|
+-------+------+
| Alex|Strong|
|Robert |Marley|
+-------+------+
作为解决方案,我创建了一个UDF,该UDF在整个Data
列上执行。作为输入参数,我传递要提取的列名和字段名。
val find_scheme_name_in_array = udf { (arr: Seq[Row], columnName: String) => {
var value = ""
arr.foreach(el =>
if(el.getAs[String]("name") == columnName){
value = el.getAs[String]("value")
}
)
value
}}
问题是我正在使用变量value
来存储中间结果,并且我不想为要执行我的UDF的每一行创建一个新变量。
我执行UDF的方式(该查询产生预期的结果):
df.select(find_scheme_name_in_array(col("Data"), lit("FName")).as("FName"),find_scheme_name_in_array(col("Data"), lit("LName")).as("LName")).show()
我很高兴听到任何有关如何改善UDF逻辑以及如何解决解析问题的不同方式的评论。
答案 0 :(得分:1)
也许这很有帮助-
val data = Seq(
"""{"Data": [{ "name": "FName", "value": "Alex" }, { "name": "LName", "value": "Strong" }]}""",
"""{"Data": [{ "name": "FName", "value": "Robert " }, { "name": "MName", "value": "Nesta " }, {
|"name": "LName", "value": "Marley" }]}""".stripMargin
)
val df = spark.read
.json(data.toDS())
df.show(false)
df.printSchema()
/**
* +----------------------------------------------------+
* |Data |
* +----------------------------------------------------+
* |[[FName, Alex], [LName, Strong]] |
* |[[FName, Robert ], [MName, Nesta ], [LName, Marley]]|
* +----------------------------------------------------+
*
* root
* |-- Data: array (nullable = true)
* | |-- element: struct (containsNull = true)
* | | |-- name: string (nullable = true)
* | | |-- value: string (nullable = true)
*/
df.selectExpr("inline_outer(Data)")
.groupBy()
.pivot("name")
.agg(collect_list("value"))
.withColumn("x", arrays_zip($"FName", $"LName"))
.selectExpr("inline_outer(x)")
.show(false)
/**
* +-------+------+
* |FName |LName |
* +-------+------+
* |Alex |Strong|
* |Robert |Marley|
* +-------+------+
*/
答案 1 :(得分:0)
我通过用foreach
方法代替find
循环解决了这个问题:
val find_scheme_name_in_array = udf { (arr: Seq[Row], columnName: String) =>
arr.find(_.getAs[String]("name") == columnName) match {
case Some(i) => i.getAs[String]("value")
case None => null
}
}