【问题标题】:How to parse a pandas column of JSON content efficiently?如何有效地解析 JSON 内容的 pandas 列?
【发布时间】:2018-10-07 00:06:45
【问题描述】:

假设我有以下 DataFrame,其中 data 列包含一个嵌套的 JSON 字符串,我想将其解析为单独的列:

import pandas as pd

df = pd.DataFrame({
    'bank_account': [101, 102, 201, 301],
    'data': [
        '{"uid": 100, "account_type": 1, "account_data": {"currency": {"current": 1000, "minimum": -500}, "fees": {"monthly": 13.5}}, "user_name": "Alice"}',
        '{"uid": 100, "account_type": 2, "account_data": {"currency": {"current": 2000, "minimum": 0},  "fees": {"monthly": 0}}, "user_name": "Alice"}',
        '{"uid": 200, "account_type": 1, "account_data": {"currency": {"current": 3000, "minimum": 0},  "fees": {"monthly": 13.5}}, "user_name": "Bob"}',        
        '{"uid": 300, "account_type": 1, "account_data": {"currency": {"current": 4000, "minimum": 0},  "fees": {"monthly": 13.5}}, "user_name": "Carol"}'        
    ]},
    index = ['Alice', 'Alice', 'Bob', 'Carol']
)


df

我找到了json_normalize 函数,目前正在列表解析中解析JSON;结果是正确的,但这需要 long。 1000 行需要 1-2 秒,而我在实际运行中大约有 100 万行:

import json
from pandas.io.json import json_normalize

parsed_df = pd.concat([json_normalize(json.loads(js)) for js in df['data']])

parsed_df['bank_account'] = df['bank_account'].values
parsed_df.index = parsed_df['user_id']

parsed_df

有没有更快的方法将这些数据解析成漂亮的 DataFrame?

【问题讨论】:

    标签: python json pandas performance


    【解决方案1】:

    我发现绕过pandas.concat 后性能略有提升(~25%)。

    否则,重写/优化json_normalize 似乎并不简单。

    def original(df):
        parsed_df = pd.concat([json_normalize(json.loads(js)) for js in df['data']])
    
        parsed_df['bank_account'] = df['bank_account'].values
        parsed_df.index = parsed_df['uid']
    
        return parsed_df
    
    def jp(df):
    
        cols = ['account_data.currency.current', 'account_data.currency.minimum',
                'account_data.fees.monthly', 'account_type', 'uid', 'user_name']
    
        parsed_df = pd.DataFrame([json_normalize(json.loads(js)).values[0] for js in df['data']],
                                 columns=cols)
    
        parsed_df['bank_account'] = df['bank_account'].values
        parsed_df.index = parsed_df['uid']
    
        return parsed_df
    
    df = pd.concat([df]*100, ignore_index=True)
    
    %timeit original(df)  # 675 ms per loop
    %timeit jp(df)        # 526 ms per loop
    

    【讨论】:

    • pd.DataFrame([...]) 通常比pd.concat([...]) 快吗?多少钱,为什么?内存使用情况差不多吗?
    • @smci,老实说,我不知道在一般情况下是否属实,或者是否特定于此用例。这可能值得提出一个新的、具体的问题!
    • @Abhijeet Joshi(他没有足够的声誉)想写:我尝试了这个解决方案,但出现错误,因为“TypeError:'int' object is not iterable”
    【解决方案2】:

    要自动获取数据框的列名,请使用:

    parsed_df = pd.DataFrame([json_normalize(json.loads(js)).values[0] for js in df['data']], columns=json_normalize(json.loads(js)).keys().tolist(), index=df.index)
    

    【讨论】:

      【解决方案3】:

      假设 JSON 数据以一大块的形式提供而不是拆分成单独的字符串,然后使用json.loads,遍历结果并创建字典,最后在字典列表上创建一个 DataFrame 效果很好。将 150,000 行、30 个原始列和 6 列提取到一个新的 DataFrame 中,不到 1 秒即可完成。

      例如:

      x = json.loads('[\
      {"uid": 100, "account_type": 1, "account_data": {"currency": {"current": 1000, "minimum": -500}, "fees": {"monthly": 13.5}}, "user_name": "Alice"},\
      {"uid": 100, "account_type": 2, "account_data": {"currency": {"current": 2000, "minimum": 0},  "fees": {"monthly": 0}}, "user_name": "Alice"},\
      {"uid": 200, "account_type": 1, "account_data": {"currency": {"current": 3000, "minimum": 0},  "fees": {"monthly": 13.5}}, "user_name": "Bob"},\
      {"uid": 300, "account_type": 1, "account_data": {"currency": {"current": 4000, "minimum": 0},  "fees": {"monthly": 13.5}}, "user_name": "Carol"}]')
      load_items = []
      for item in x:
        load_items.append({
          'uid': item['uid'],
          'account_type': item['account_type'],
          'currency_current': item['account_data']['currency']['current'],
          'currency_minimum': item['account_data']['currency']['minimum'],
          'fees_monthly': item['account_data']['fees']['monthly'],
          'user_name': item['user_name'], 
        })
      y = pd.DataFrame(load_items)
      y
      
      index uid account_type currency_current currency_minimum fees_monthly user_name
      0 100 1 1000 -500 13.5 Alice
      1 100 2 2000 0 0.0 Alice
      2 200 1 3000 0 13.5 Bob
      3 300 1 4000 0 13.5 Carol

      (*感谢 Jupyter/Colab 提供的降价表)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-21
        相关资源
        最近更新 更多