【问题标题】:Remove nested JSON from DataFrame without creating new rows or duplicate data从 DataFrame 中删除嵌套的 JSON,而不创建新行或重复数据
【发布时间】:2020-07-13 15:48:13
【问题描述】:

概览

使用pandas.json_normalize,我能够将具有多个嵌套对象的 BigQuery 输出的 JSON 数据规范化到一个数据框中。 csv 表示如下。你会注意到它应该爆发的“设备”之类的东西。在其他情况下,例如该列和event_params 它没有。我到处玩,能够扩展这个对象,但它会引入新的角色,幸运的是,我只需要一个取决于 event_name 值的键/值。对于本例,我想提取 session_user_id 并将其规范化为类似的宽列布局。

CSV 数据

event_date,event_timestamp,event_name,event_params,device.operating_system  device.operating_system_version
20200105,69996099900,session_start,[{'key': 'ga_session_number', 'value': {'string_value': None, 'int_value': '3', 'float_value': None, 'double_value': None}}, {'key': 'session_engaged', 'value': {'string_value': None, 'int_value': '1', 'float_value': None, 'double_value': None}}, {'key': 'engaged_session_event', 'value': {'string_value': None, 'int_value': '1', 'float_value': None, 'double_value': None}}, {'key': 'session_id', 'value': {'string_value': None, 'int_value': '123456789', 'float_value': None, 'double_value': None}}, {'key': 'firebase_event_origin', 'value': {'string_value': 'auto', 'int_value': None, 'float_value': None, 'double_value': None}}],IOS,9.3.5

问题

我可能只是不知所措,但是有没有一种简单的方法可以将具有关联值的单个(或更简单的所有键)提取到其他列中?

例如,新列将是:

(老实说,键/值的命名只要是宽 csv 平面文件就不是问题)

event_params.1.key = session_engaged
event_params.1.string_value = None
event_params.1.int_value = 1
[...]

event_params.2.key = session_id
event_params.2.string_value = None
event_params.2.int_value = 123456789
[...]

【问题讨论】:

    标签: python pandas dataframe data-structures data-science


    【解决方案1】:

    这个答案虽然不是很优雅,而且可能需要一些工作才能得到你想要的,但它是一个合理的开始。

    设置数据

    我采用了您的示例数据并稍微修改了格式,以将其加载到dataframe

    columns = 'event_date event_timestamp event_name event_params device.operating_system device.operating_system_version'.split()
    data = [
            20200105,
            69996099900,
            'session_start',
            [{'key': 'ga_session_number', 'value': {'string_value': None, 'int_value': '3', 'float_value': None, 'double_value': None}}, {'key': 'session_engaged', 'value': {'string_value': None, 'int_value': '1', 'float_value': None, 'double_value': None}}, {'key': 'engaged_session_event', 'value': {'string_value': None, 'int_value': '1', 'float_value': None, 'double_value': None}}, {'key': 'session_id', 'value': {'string_value': None, 'int_value': '123456789', 'float_value': None, 'double_value': None}}, {'key': 'firebase_event_origin', 'value': {'string_value': 'auto', 'int_value': None, 'float_value': None, 'double_value': None}}],
            'IOS',
            '9.3.5',
            ]
    
    df = pd.DataFrame([data], columns=columns)
    

    初始数据:

       event_date  event_timestamp     event_name                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            event_params device.operating_system device.operating_system_version
    0    20200105      69996099900  session_start  [{'key': 'ga_session_number', 'value': {'string_value': None, 'int_value': '3', 'float_value': None, 'double_value': None}}, {'key': 'session_engaged', 'value': {'string_value': None, 'int_value': '1', 'float_value': None, 'double_value': None}}, {'key': 'engaged_session_event', 'value': {'string_value': None, 'int_value': '1', 'float_value': None, 'double_value': None}}, {'key': 'session_id', 'value': {'string_value': None, 'int_value': '123456789', 'float_value': None, 'double_value': None}}, {'key': 'firebase_event_origin', 'value': {'string_value': 'auto', 'int_value': None, 'float_value': None, 'double_value': None}}]                     IOS                           9.3.5
    

    解决方案

    new_df = df.explode('event_params').event_params.apply(pd.Series).value.apply(pd.Series)
    df = pd.concat([df.drop('event_params', axis=1), new_df], axis=1)
    

    输出:

       event_date  event_timestamp     event_name device.operating_system device.operating_system_version string_value  int_value float_value double_value
    0    20200105      69996099900  session_start                     IOS                           9.3.5         None          3        None         None
    0    20200105      69996099900  session_start                     IOS                           9.3.5         None          1        None         None
    0    20200105      69996099900  session_start                     IOS                           9.3.5         None          1        None         None
    0    20200105      69996099900  session_start                     IOS                           9.3.5         None  123456789        None         None
    0    20200105      69996099900  session_start                     IOS                           9.3.5         auto       None        None         None
    

    说明

    我将第一行的描述分解成多个步骤:

    1. 只需使用explode 即可访问event_params 中的列表内部(如果愿意,您可以使用apply lambda 函数)。
    2. 首先使用apply.(pd.Series) 扩展JSON see here for more info
    3. 再次使用apply.(pd.Series) 展开values 列。

    当然,第二行只是将两个数据帧连接在一起,event_params 从原始数据中删除。

    【讨论】:

    • 谢谢,我对这种方法的担忧是它会创建新行,因此会为某些列重复记录——尽管正如你所建议的那样,这可能足以让我开始。我只是不是 100% 将其分解为具有相同行数的单个平面结构
    • 目前我无法在这方面花费更多时间,但请查看用于 padas 数据帧的 pivotpivot_table 方法。
    猜你喜欢
    • 2019-08-20
    • 2019-06-21
    • 1970-01-01
    • 2014-02-24
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多