【问题标题】:Using pandas.json_normalize to "unfold" a dictionary of a list of dictionaries使用 pandas.json_normalize 来“展开”字典列表的字典
【发布时间】:2021-12-06 19:19:00
【问题描述】:

我是 Python 新手(和一般的编码),所以我会尽力解释我正在努力解决的挑战。

我正在处理一个从数据库导出为 CSV 的大型数据集。但是,此 CSV 导出中有一个列包含字典的嵌套列表(据我所知)。我已经在网上广泛寻找解决方案,包括在 Stackoverflow 上,但还没有得到完整的解决方案。我想我从概念上理解我想要完成的工作,但不清楚要使用的最佳方法或数据准备过程。

这是一个数据示例(缩减为我感兴趣的两列):

    {
       "app_ID": {
          "0": 1abe23574,
          "1": 4gbn21096
       },
       "locations": {
          "0": "[ {"loc_id" : "abc1",  "lat" : "12.3456",  "long" : "101.9876"  
                  },
                  {"loc_id" : "abc2",  "lat" : "45.7890",  "long" : "102.6543"} 
                ]",
          "1": "[ ]",
         ]"
       }
    }

基本上,每个 app_ID 可以有多个位置绑定到一个 ID,或者它可以是空的,如上所示。我尝试使用我在网上找到的一些指南,使用 Panda 的 json_normalize() 函数来“展开”或将字典列表放入 Panda 数据框中自己的行中。

我希望得到这样的结果:

loc_id    lat      long       app_ID
abc1      12.3456  101.9876   1abe23574
abc1      45.7890  102.6543   1abe23574

等等……

我正在学习如何使用 json_normalize 的不同功能,例如“record_path”和“meta”,但还不能让它工作。

我尝试使用以下方法将 json 文件加载到 Jupyter Notebook 中:

with open('location_json.json', 'r') as f:
          data = json.loads(f.read())
df = pd.json_normalize(data, record_path = ['locations'])

但它只创建一个 1 行和多列长的数据框,我希望从最里面的字典生成多行,这些行与 app_ID 和 loc_ID 字段相关联。

尝试解决方案:

我能够接近我想要使用的数据帧格式:

with open('location_json.json', 'r') as f:
          data = json.loads(f.read())
df = pd.json_normalize(data['locations']['0'])

但这需要对列表进行某种迭代才能创建数据框,然后我将失去与 app_ID 字段的连接。 (尽我所能理解 json_normalize 函数的工作原理)。

我是否在尝试使用 json_normalize 的正确轨道上,还是应该重新开始尝试不同的路线?任何建议或指导将不胜感激。

【问题讨论】:

    标签: python pandas dataframe dictionary json-normalize


    【解决方案1】:

    我不能说建议您使用convtools 库是一件好事,因为您是初学者,因为这个库几乎就像 Python 之上的另一个 Python。它有助于动态定义数据转换(在后台生成 Python 代码)。

    但无论如何,如果我正确理解输入数据,这里是代码:

    import json
    from convtools import conversion as c
    
    data = {
        "app_ID": {"0": "1abe23574", "1": "4gbn21096"},
        "locations": {
            "0": """[ {"loc_id" : "abc1",  "lat" : "12.3456",  "long" : "101.9876" },
                  {"loc_id" : "abc2",  "lat" : "45.7890",  "long" : "102.6543"} ]""",
            "1": "[ ]",
        },
    }
    
    # define it once and use multiple times
    converter = (
        c.join(
            # converts "app_ID" data to iterable of dicts
            (
                c.item("app_ID")
                .call_method("items")
                .iter({"id": c.item(0), "app_id": c.item(1)})
            ),
            # converts "locations" data to iterable of dicts,
            # where each id like "0" is zipped to each location.
            # the result is iterable of dicts like {"id": "0", "loc": {"loc_id": ... }}
            (
                c.item("locations")
                .call_method("items")
                .iter(
                    c.zip(id=c.repeat(c.item(0)), loc=c.item(1).pipe(json.loads))
                )
                .flatten()
            ),
            # join on "id"
            c.LEFT.item("id") == c.RIGHT.item("id"),
            how="full",
        )
        # process results, where 0 index is LEFT item, 1 index is the RIGHT one
        .iter(
            {
                "loc_id": c.item(1, "loc", "loc_id", default=None),
                "lat": c.item(1, "loc", "lat", default=None),
                "long": c.item(1, "loc", "long", default=None),
                "app_id": c.item(0, "app_id"),
            }
        )
        .as_type(list)
        .gen_converter()
    )
    result = converter(data)
    
    assert result == [
        {'loc_id': 'abc1', 'lat': '12.3456', 'long': '101.9876', 'app_id': '1abe23574'},
        {'loc_id': 'abc2', 'lat': '45.7890', 'long': '102.6543', 'app_id': '1abe23574'},
        {'loc_id': None, 'lat': None, 'long': None, 'app_id': '4gbn21096'}
    ]
    
    
    

    【讨论】:

    • 谢谢 Nikita,我一定会看看这个!
    猜你喜欢
    • 1970-01-01
    • 2021-09-07
    • 2019-01-11
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    相关资源
    最近更新 更多