【问题标题】:Deleting nested array entries in a DataFrame (JSON) on a condition根据条件删除 DataFrame (JSON) 中的嵌套数组条目
【发布时间】:2017-11-16 17:44:18
【问题描述】:

我在一个 DataFrame 中读取了一个巨大的文件,它的每一行都包含一个 JSON 对象,如下所示:

{
  "userId": "12345",
  "vars": {
    "test_group": "group1",
    "brand": "xband"
  },
  "modules": [
    {
      "id": "New"
    },
    {
      "id": "Default"
    },
    {
      "id": "BestValue"
    },
    {
      "id": "Rating"
    },
    {
      "id": "DeliveryMin"
    },
    {
      "id": "Distance"
    }
  ]
}

我怎样才能以这种方式操作 DataFrame,只保留具有 id="Default" 的模块?如果 id 不等于 "Default",如何删除所有其他?

【问题讨论】:

    标签: scala apache-spark dataframe rdd


    【解决方案1】:

    正如您所说,您在每一行中都有json 格式的问题

    {"userId":"12345","vars":{"test_group":"group1","brand":"xband"},"modules":[{"id":"New"},{"id":"Default"},{"id":"BestValue"},{"id":"Rating"},{"id":"DeliveryMin"},{"id":"Distance"}]}
    {"userId":"12345","vars":{"test_group":"group1","brand":"xband"},"modules":[{"id":"New"},{"id":"Default"},{"id":"BestValue"},{"id":"Rating"},{"id":"DeliveryMin"},{"id":"Distance"}]}
    

    如果是这样,那么您可以使用sqlContext 的json api 将json 文件读取到dataframe,如下所示

    val df = sqlContext.read.json("path to json file")
    

    应该给你dataframe

    +--------------------------------------------------------------------+------+--------------+
    |modules                                                             |userId|vars          |
    +--------------------------------------------------------------------+------+--------------+
    |[[New], [Default], [BestValue], [Rating], [DeliveryMin], [Distance]]|12345 |[xband,group1]|
    |[[New], [Default], [BestValue], [Rating], [DeliveryMin], [Distance]]|12345 |[xband,group1]|
    +--------------------------------------------------------------------+------+--------------+
    

    和schema 是

    root
     |-- modules: array (nullable = true)
     |    |-- element: struct (containsNull = true)
     |    |    |-- id: string (nullable = true)
     |-- userId: string (nullable = true)
     |-- vars: struct (nullable = true)
     |    |-- brand: string (nullable = true)
     |    |-- test_group: string (nullable = true)
    

    最后一步将是 filter 仅将 modules.id 与 Default 作为值

    val finaldf = df.withColumn("modules", explode($"modules.id"))
        .filter($"modules" === "Default")
    

    这应该给你

    +-------+------+--------------+
    |modules|userId|vars          |
    +-------+------+--------------+
    |Default|12345 |[xband,group1]|
    |Default|12345 |[xband,group1]|
    +-------+------+--------------+
    

    希望回答对你有帮助

    更新

    这将创建json

    {"modules":"Default","userId":"12345","vars":{"brand":"xband","test_group":"group1"}}
    {"modules":"Default","userId":"12345","vars":{"brand":"xband","test_group":"group1"}}
    

    但如果你的要求是得到如下

    {"modules":{"id":"Default"},"userId":"12345","vars":{"brand":"xband","test_group":"group1"}}
    {"modules":{"id":"Default"},"userId":"12345","vars":{"brand":"xband","test_group":"group1"}}
    

    你应该爆炸modules而不是modules.id

    val finaldf = df.withColumn("modules", explode($"modules"))
        .filter($"modules.id" === "Default")
    

    【讨论】:

    • 这接近解决方案,但不幸的是,如果我在此之后编写 JSON 文件,它将在模块数组中具有“默认”作为简单的字符串元素。请参阅:...“modules”:“Default”,...我想将它作为 JSON 元素保留在 {} 中作为“modules”:{“id”:“Default”} 我不想丢失结构/模式。
    • 支持并接受您的回答。您是否也有一个在 filter() 内没有隐含的解决方案?我的意思是不要使用 $ 和 ===
    • 别在意以上。我应该如何传入字符串列表并过滤多个模块?类似于:filter(validModules.contains($"modules.id"))
    • 您应该为此使用 udf 函数:) 请为此提出另一个问题,因为该答案足以解决该问题。 :)
    • 定义一个数组val validModules = Array("Dfault", "default")定义一个udf函数def contains = udf((list: mutable.WrappedArray[String], string: String) => list.contains(string))并在filter中使用udf函数为.filter(contains(lit(validModules), $"modules.id"))。
    猜你喜欢
    • 2016-01-18
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 2023-01-20
    • 2018-01-11
    • 1970-01-01
    相关资源
    最近更新 更多