Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.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
Arrays Spark-Scala嵌套数组DF-如何在不改变结构的情况下根据条件更新值? 我有一个类似于以下orc/拼花地板格式的结构。_Arrays_Scala_Dataframe_Apache Spark_Struct - Fatal编程技术网

Arrays Spark-Scala嵌套数组DF-如何在不改变结构的情况下根据条件更新值? 我有一个类似于以下orc/拼花地板格式的结构。

Arrays Spark-Scala嵌套数组DF-如何在不改变结构的情况下根据条件更新值? 我有一个类似于以下orc/拼花地板格式的结构。,arrays,scala,dataframe,apache-spark,struct,Arrays,Scala,Dataframe,Apache Spark,Struct,我需要根据Apt=Apt1的条件创建一个新的DF,并将该条目的电话号码更改为7777。 注意:需要保持相同的结构。 我在scala spark中尝试了两种方法,但无法更新嵌套数组结构类型。任何专家的建议都会有帮助 更新:通过这个链接,我可以获得命名的结构变量。说到数组,我无法得到答案。 第一步是在数据帧中映射json, 然后,我们创建一个自定义UDF,该UDF接受Apt列、PhoneNum列的输入,如果Apt=Apt1,则新电话号码允许更改电话号码 def main(args: Array[

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

更新:通过这个链接,我可以获得命名的结构变量。说到数组,我无法得到答案。
第一步是在数据帧中映射json, 然后,我们创建一个自定义UDF,该UDF接受Apt列、PhoneNum列的输入,如果Apt=Apt1,则新电话号码允许更改电话号码

  def main(args: Array[String]): Unit = {


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


    import sparkSession.implicits._

    val outputDataFrame = sparkSession.read.option("multiline", true).option("mode","PERMISSIVE")
      .json(Seq(inputJson).toDS)
      .select(
          // First layer mapping
          col("Register").getItem("Persons").as("Persons")
        )
      .withColumn("Persons", explode(col("Persons")))
        .select(
          // Second layer mapping
          col("Persons").getItem("Name").as("Name"),
          col("Persons").getItem("Age").as("Age"),
          col("Persons").getItem("Address").as("Address"),
          col("Persons").getItem("Phone").as("Phone")
        )
        .select(col("Name"),col("Age"),
          // last layer mapping
          col("Address").getItem("Apt").as("Apt"),
          col("Phone").getItem("PhoneNum").as("PhoneNum"))
        .withColumn("Apt", explode(col("Apt")))
        .withColumn("PhoneNum", explode(col("PhoneNum")))
        .withColumn("PhoneNum", changePhoneNumUDF(col("Apt"), col("PhoneNum"), lit(987654))) // apply user defined function to change PhoneNume according to Apt

    outputDataFrame.show


  }
  def changePhoneNum(Apt : String, oldPhoneNum : Long ,NewPhoneNum : Long) : Long = Apt match {
    case "Apt1" => NewPhoneNum
    case _ => oldPhoneNum
  }
  val changePhoneNumUDF = udf(changePhoneNum _)
}
输出:

+-----+---+----+--------+
| Name|Age| Apt|PhoneNum|
+-----+---+----+--------+
|Name1| 12|Apt1|  987654|
|Name2| 14|Apt2|   55555|
+-----+---+----+--------+
其思想是将嵌套结构转换为一组更容易处理的简单Scala类,或者用Spark术语:使用类型化的数据帧而不是非类型化的数据帧。

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

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)
关于问题中数据结构/模式的备注:


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

数据帧的每个顶级条目是Register还是Person?您可以发布df.printSchema的输出吗?|-Register:struct nullable=true | | | |-Persons:array nullable=true | | | | |-element:struct containsnall=true | | | | | | Apt:string nullable=true ||| |-年龄:long nullable=true | | | | | | | | | | | |-Name:string nullable=true | | | | | |-PhoneNum:true@werner这是示例,我已经给出了模式。我尝试从现有的DF中重新创建一个类似于以下内容的DF,但无法替换嵌套数组中的现有值。选择exp named_struct Register、named_struct{Persons、arraynamed_struct{Name、Register.Persons.Name、Phone、Register.Persons.Phone}.as named_struct.select$named_struct.RegisterSRV,对您有帮助吗?感谢Simba的输入;对于我来说,explode不适合,我需要保留相同的JSON类型嵌套结构库。第二部分,正如您所提到的,它可能不是数组的第一个元素。如果一个人有多个地址和/或地址,那么预期的行为会是什么r不止一个电话号码?非常感谢Werner。逻辑工作得很好。但我有一个问题,比如,我不能在case类中有一个固定的模式,这将在实际场景中发生变化。您可能想重写您的问题,或者问一个新的问题,更详细地描述什么是固定的以及更新数据时的条件。
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)
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"}]}]}}|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+