【问题标题】:Flatting a JSON file into Pandas Dataframe in Python在 Python 中将 JSON 文件扁平化为 Pandas Dataframe
【发布时间】:2022-01-12 11:29:10
【问题描述】:

我有这种格式的 json:

{
    "fields": {
        "tcidte": {
            "mode": "required",
            "type": "date",
            "format": "%Y%m%d"
        },
        "tcmcid": {
            "mode": "required",
            "type": "string"
        },
        "tcacbr": {
            "mode": "required",
            "type": "string"
        }
    }
}

我希望它采用数据框格式,其中三个字段名称中的每一个都是单独的行。如果一行有一列(例如“格式”),其他为空白的,则应假定为 NULL。

我已尝试使用我在此处找到的 flatten_json 函数,但无法按预期工作,但仍会在此处包含:

def flatten_json(nested_json, exclude=['']):
    """Flatten json object with nested keys into a single level.
        Args:
            nested_json: A nested json object.
            exclude: Keys to exclude from output.
        Returns:
            The flattened json object if successful, None otherwise.
    """
    out = {}

    def flatten(x, name='', exclude=exclude):
        if type(x) is dict:
            for a in x:
                if a not in exclude: flatten(x[a], name + a + '_')
        elif type(x) is list:
            i = 0
            for a in x:
                flatten(a, name + str(i) + '_')
                i += 1
        else:
            out[name[:-1]] = x

    flatten(nested_json)
    return out

flatten_json_file = pd.DataFrame(flatten_json(nested_json))
pprint.pprint(flatten_json_file)

额外的复杂性 JSON:

{
    "fields": {
        "action": {
            "type": {
                "field_type": "string"
            },
            "mode": "required"
        },
        "upi": {
            "type": {
                "field_type": "string"
            },
            "regex": "^[0-9]{9}$",
            "mode": "required"
        },
        "firstname": {
            "type": {
                "field_type": "string"
            },
            "mode": "required"
        }
    }
}

【问题讨论】:

    标签: python json pandas dataframe json-flattener


    【解决方案1】:

    data = {
        "fields": {
            "tcidte": {
                "mode": "required",
                "type": "date",
                "format": "%Y%m%d"
            },
            "tcmcid": {
                "mode": "required",
                "type": "string"
            },
            "tcacbr": {
                "mode": "required",
                "type": "string"
            }
        }
    }
    

    这个

    df = pd.DataFrame(data["fields"].values())
    

    结果

           mode    type  format
    0  required    date  %Y%m%d
    1  required  string     NaN
    2  required  string     NaN
    

    这是你的目标吗?

    如果要将data["fields"] 的键作为索引:

    df = pd.DataFrame(data["fields"]).T
    

    df = pd.DataFrame.from_dict(data["fields"], orient="index")
    

    两者都导致

                mode    type  format
    tcidte  required    date  %Y%m%d
    tcmcid  required  string     NaN
    tcacbr  required  string     NaN
    

    data = {
        "fields": {
            "action": {
                "type": {
                    "field_type": "string"
                },
                "mode": "required"
            },
            "upi": {
                "type": {
                    "field_type": "string"
                },
                "regex": "^[0-9]{9}$",
                "mode": "required"
            },
            "firstname": {
                "type": {
                    "field_type": "string"
                },
                "mode": "required"
            }
        }
    }
    

    你可以这样做

    data = {key: {**d, **d["type"]} for key, d in data["fields"].items()}
    df = pd.DataFrame.from_dict(data, orient="index").drop(columns="type")
    

    df = pd.DataFrame.from_dict(data["fields"], orient="index")
    df = pd.concat(
        [df, pd.DataFrame(df.type.to_list(), index=df.index)], axis=1
    ).drop(columns="type")
    

    结果如下(列位置可能不同)

                   mode field_type       regex
    action     required     string         NaN
    upi        required     string  ^[0-9]{9}$
    firstname  required     string         NaN
    

    【讨论】:

    • 很酷的解决方案,但列需要具有字段名称,例如“tcidte”而不是索引
    • @Elliot_J 你的意思是你想要data["fields"] 键作为索引:我刚刚更新了我的答案以包含该选项。
    • 您的解决方案非常好,又好又简单。但是,它可以处理进一步嵌套 dic 的额外复杂性吗?我已经用新的 JSON 更新了原始问题
    • @Elliot_J 并非没有进一步的工作。我添加了 2 个可能性。
    【解决方案2】:
    df= pd.read_json('test.json')
    df_fields = pd.DataFrame(df['fields'].values.tolist(), index=df.index)
    print(df_fields)
    

    输出:

                mode    type  format
    tcacbr  required  string     NaN
    tcidte  required    date  %Y%m%d
    tcmcid  required  string     NaN
    

    【讨论】:

      【解决方案3】:

      一个选项是jmespath 库,它可以在以下场景中提供帮助:

      # pip install jmespath
      import jmespath
      import pandas as pd
      
      # think of it like a path 
      # fields is the first key
      # there are sub keys with varying names
      # we are only interested in mode, type, format
      # hence the * to represent the intermediate key(s)
      expression = jmespath.compile('fields.*[mode, type, format]')
      
      pd.DataFrame(expression.search(data), columns = ['mode', 'type', 'format'])
      
             mode    type  format
      0  required    date  %Y%m%d
      1  required  string    None
      2  required  string    None
      

      jmespath 有很多工具;然而,这应该就足够了,并且涵盖了子词典中缺少键(模式、类型、格式)的情况。

      【讨论】:

      • 我已经为 jmespath 运行了 pip 安装。但是当我运行时出现以下错误:(expression = jmespath.compile('fields.*[mode, type, format]')) AttributeError: module 'jmespath' has no attribute 'compile'
      • 我在 Python 3.6 版上运行
      • 试用 3.8 版
      猜你喜欢
      • 2021-05-24
      • 1970-01-01
      • 2019-04-24
      • 1970-01-01
      • 2018-05-14
      • 2018-03-11
      • 2019-12-01
      • 2017-12-16
      • 2021-08-15
      相关资源
      最近更新 更多