如何有效地解析 JSON 内容的 pandas 列?
Posted
技术标签:
【中文标题】如何有效地解析 JSON 内容的 pandas 列?【英文标题】:How to parse a pandas column of JSON content efficiently? 【发布时间】: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?
【问题讨论】:
【参考方案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 提供的降价表)
【讨论】:
以上是关于如何有效地解析 JSON 内容的 pandas 列?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 pandas 数据帧中有效地使用 one-hot 编码规范化列?
使用包含嵌套 JSON 字符串的一列解析 Pandas DataFrame 中的列