首页 > 解决方案 > Spark -Scala 嵌套数组 DF - 如何在不改变结构的情况下根据条件更新值?

问题描述

我有一个类似以下的 orc/parquet 格式的结构。

{
  "Register": {
    "Persons": [
      {
        "Name": "Name1",
        "Age": 12,
        "Address": [
          {
            "Apt": "Apt1"
          }
        ],
        "Phone": [
          {
            "PhoneNum": 1234
          }
        ]
      },
      {
        "Name": "Name2",
        "Age": 14,
        "Address": [
          {
            "Apt": "Apt2"
          }
        ],
        "Phone": [
          {
            "PhoneNum": 55555
          }
        ]
      }

    ]
  }
}

我需要根据条件 Apt= Apt1 创建一个新的 DF,并将该条目的电话号码更改为 7777。注意:需要保持相同的结构。我在 scala-spark 中尝试了几种方法,但无法更新嵌套数组结构类型。任何专家的建议都会有所帮助。

更新:按照这个链接,我可以得到 named_struct 变量。说到数组,我无法得到答案。 https://kb.databricks.com/data/update-nested-column.html#how-to-update-nested-columns

标签: arraysscaladataframeapache-sparkstruct

解决方案


这个想法是使用案例类将嵌套结构转换为一组更容易处理的简单 Scala 类 - 或者用 Spark 术语来说:使用(类型化)数据集而不是非类型化 DataFrame。

case class Phone(var PhoneNum:String)
case class Apt(Apt:String)
case class Person(Name: String, Age: Long, Address:Array[Apt], Phone:Array[Phone])
case class Register(Persons:Array[Person])
case class TopLevel(Register:Register)

将数据框转换为数据集,然后对map数据集的每个条目应用调用:

val df = ...
val ds = df.as[TopLevel]
val transformed = ds.map(tl => {
  for( p <- tl.Register.Persons) {
    if(p.Address.contains(Apt("Apt1"))) p.Phone.transform(_ => Phone("7777"))
  }
  tl
})
transformed.toJSON.show(false)

印刷:

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|value                                                                                                                                                                                            |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|{"Register":{"Persons":[{"Name":"Name1","Age":12,"Address":[{"Apt":"Apt1"}],"Phone":[{"PhoneNum":"7777"}]},{"Name":"Name2","Age":14,"Address":[{"Apt":"Apt2"}],"Phone":[{"PhoneNum":"55555"}]}]}}|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

关于问题中的数据结构/模式的评论

提出问题时,使用了寄存器数据帧。这意味着数据帧的每个条目都包含一个寄存器。如果数据框包含人员列表并且此人员列表称为“注册”,则会更直观。这将导致数据结构更加简单。在这种情况下,类TopLevelRegister可以省略。


推荐阅读