![](/img/trans.png)
[英]Most efficient way to convert a dictionary with list of numpy arrays into pandas dataframe?
[英]Most Efficient Way to Convert a Complex List of Dictionaries Into a Pandas Dataframe
我在event_records
中有一个字典列表,下面是该列表的一个子集。 每个字典包含 2 或 3 个键值对。 第一个键是item
,对应的值是event#status
。
第二个键是count
,对应的值由包含 8 个键值对 + 1 个键值对的字典组成,其中值是 9 个字典的列表,每个字典包含 3 个键值对。
第三个键(仅在某些时候出现)是errors
,对应的值是一个字典,列表中有 3 个键值对。
将event_records
中的以下字典列表转换为 pandas dataframe 的最有效方法是什么? 我尝试了以下代码,但是速度和性能都很慢。
from pandas.io.json import json_normalize
import pandas as pd
df1 = json_normalize(event_records)
df2 = df1['customEvents']
custom_events_list = []
for element in df2:
df3 = json_normalize(element)
df4 = df3[['type', 'value']]
df5 = df4.T
df5.columns = df5.iloc[0]
df5 = df5[1:]
custom_events_list.append(df5)
df6 = pd.concat(custom_events_list)
df6 = df6.reset_index(drop = True)
df7 = df1.join(df6)
df8 = df1['errors']
event_error_list = []
for element in df8:
df9 = json_normalize(element)
df10 = df9[['response', 'feedback']]
event_error_list.append(df10)
df11 = pd.concat(event_error_list)
df11 = df11.reset_index(drop = True)
df12 = df7.join(df11)
df13 = df12[['old_id', 'new_id', 'event_id', 'event_time', 'value', 'quantity', 'unique_id', 'A3', 'A4', 'A6', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'response', 'feedback']]
event_records = [{'item': 'event#status',
'count': {'item': 'event#count',
'old_id': '123',
'new_id': '456',
'event_id': '111',
'event_time': '1200',
'value': 1.0,
'quantity': '1',
'unique_id': '222',
'customEvents': [{'item': 'event#custom', 'type': 'A3', 'value': ''},
{'item': 'event#custom', 'type': 'A4', 'value': '11AA'},
{'item': 'event#custom', 'type': 'A6', 'value': 'AAB1'},
{'item': 'event#custom', 'type': 'A9', 'value': ''},
{'item': 'event#custom', 'type': 'A10', 'value': '10.5'},
{'item': 'event#custom', 'type': 'A11', 'value': 'ABC'},
{'item': 'event#custom', 'type': 'A12', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A13', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A14', 'value': 'NYR'}]},
'errors': [{'item': 'event#Error',
'response': 'NONE',
'feedback': 'Event not found'}]},
{'item': 'event#status',
'count': {'item': 'event#count',
'old_id': '567',
'new_id': '789',
'event_id': '333',
'event_time': '1400',
'value': 1.0,
'quantity': '1',
'unique_id': '444',
'customEvents': [{'item': 'event#custom', 'type': 'A3', 'value': ''},
{'item': 'event#custom', 'type': 'A4', 'value': '22BB'},
{'item': 'event#custom', 'type': 'A6', 'value': 'CCD1'},
{'item': 'event#custom', 'type': 'A9', 'value': ''},
{'item': 'event#custom', 'type': 'A10', 'value': '20.5'},
{'item': 'event#custom', 'type': 'A11', 'value': 'ABC'},
{'item': 'event#custom', 'type': 'A12', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A13', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A14', 'value': 'NYR'}]}}]
所需的Pandas dataframe output如下:
old_id new_id event_id event_time value quantity unique_id A3 A4 A6 A9 A10 A11 A12 A13 A14 response feedback
123 456 111 1200 1.0 1 222 11AA AAB1 10.5 ABC NYR NYR NYR NONE Event not found
567 789 333 1400 1.0 1 444 22BB CCD1 20.5 ABC NYR NYR NYR
添加到数据帧是一个缓慢的过程,因为每次添加都会重新创建整个 object。 在您的代码中,您创建 13 个数据框。 我建议您在数据框 object 之外进行所有格式化,然后一举创建数据框。 有多种方法可以创建数据框(有关一些示例,请参阅此geeks for geeks 页面),您可以选择最适合您的方法
对我来说似乎最快的方法是遍历事件记录列表,如下所示:
processed_records = []
for event_record in event_records:
processed_records.append(process_record(event_record))
df = pd.DataFrame(processed_records)
然后你需要编写一个名为“process_record”的function)从事件记录中提取所有相关数据并以字典格式返回(例如{"old_id": 123, "new_id": 345, "event_id": 567..."feedback": None}
)。 您必须注意一些怪癖。 由于某些记录没有错误,因此您需要确保添加“无”或-1 或其他值以在此列上指示 null 值。 否则,您将在 pandas 的列中看到“Nan”。 这将需要一些乏味的代码,但它会比创建 12 个不必要的数据帧的版本快得多。
编辑:澄清代码
由于 pandas json_normalize 和列表理解,这里的数据处理非常优雅。
首先提取自定义事件
parent_fields = ['old_id', 'new_id', 'event_id', 'event_time', 'value', 'quantity', 'unique_id']
custom_events = json_normalize(
[r['count'] for r in event_records],
'customEvents',
parent_fields,
record_prefix='#'
)
然后提取errors
。 在这里,我使用了一个鲜为人知的列表推导功能,它允许对嵌套元素进行过滤和迭代,以生成输入 DataFrame 构造函数的记录
errors = pd.DataFrame(
[(e['response'], e['feedback'],r['count']['unique_id'])
for r in event_records if 'errors' in r
for e in r['errors']],
columns=['response', 'feedback', 'unique_id'])
合并两个数据框
df = custom_events.merge(
errors,
left_on='unique_id',
right_on='unique_id',
how='left'
)
shaped = df.set_index(
[c for c in df.columns if c != '#value']
).unstack('#type')
此时,shape 是具有所需shaped
的 dataframe,但是列仍然是多索引而不是平面列表。
#shaped outputs:
#value
#type A10 A11 A12 A13 A14 A3 A4 A6 A9
old_id new_id event_id event_time value quantity unique_id response feedback
123 456 111 1200 1.0 1 222 NONE Event not found 10.5 ABC NYR NYR NYR 11AA AAB1
567 789 333 1400 1.0 1 444 NaN NaN 20.5 ABC NYR NYR NYR 22BB CCD1
将列设置为多索引中的第二级并重置数据框的索引,如果您愿意,可以重新排序列
shaped.columns = shaped.columns.levels[1]
shaped.reset_index()
# outputs:
#type old_id new_id event_id event_time value quantity unique_id response feedback A10 A11 A12 A13 A14 A3 A4 A6 A9
0 123 456 111 1200 1.0 1 222 NONE Event not found 10.5 ABC NYR NYR NYR 11AA AAB1
1 567 789 333 1400 1.0 1 444 NaN NaN 20.5 ABC NYR NYR NYR 22BB CCD1
我建议我们创建三个数据框并在之后连接。 此外,一些数据嵌套在列表中,嵌套在字典中,嵌套在列表中。 一段旅程。 就个人而言,我使用库(jmespath)使旅程更轻松,恕我直言,更简单。 正如我所说,就个人而言。 开始:
import jmespath
from collections import defaultdict
首先我们为ID创建dataframe; 这里的嵌套不是那么深,列表理解(以及嵌套列表理解)应该可以解决问题,而且在这里它是一种更明智的方法:
df1 = pd.DataFrame({key:value
for key,value
in entry['count'].items()
if key not in ('customEvents','item')}
for entry in event_records)
df1
old_id new_id event_id event_time value quantity unique_id
0 123 456 111 1200 1.0 1 222
1 567 789 333 1400 1.0 1 444
拔出的第二个 dataframe 是“As”; 这就是jmespath发挥作用的地方,因为它允许轻松遍历嵌套列表/字典。 你可以在这里写一个嵌套列表理解,但是 jmespath 允许我们避免这种嵌套:
customEvents 的路径是: list -> dict -> count -> customEvents -> list<br>
键在 jmespath 中通过点 (.) 符号访问,而列表通过方括号 ([]) 符号访问
As =jmespath.compile('[].count.customEvents[]')
out = As.search(event_records)
print(out)
[{'item': 'event#custom', 'type': 'A3', 'value': ''},
{'item': 'event#custom', 'type': 'A4', 'value': '11AA'},
{'item': 'event#custom', 'type': 'A6', 'value': 'AAB1'},
{'item': 'event#custom', 'type': 'A9', 'value': ''},
{'item': 'event#custom', 'type': 'A10', 'value': '10.5'},
{'item': 'event#custom', 'type': 'A11', 'value': 'ABC'},
{'item': 'event#custom', 'type': 'A12', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A13', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A14', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A3', 'value': ''},
{'item': 'event#custom', 'type': 'A4', 'value': '22BB'},
{'item': 'event#custom', 'type': 'A6', 'value': 'CCD1'},
{'item': 'event#custom', 'type': 'A9', 'value': ''},
{'item': 'event#custom', 'type': 'A10', 'value': '20.5'},
{'item': 'event#custom', 'type': 'A11', 'value': 'ABC'},
{'item': 'event#custom', 'type': 'A12', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A13', 'value': 'NYR'},
{'item': 'event#custom', 'type': 'A14', 'value': 'NYR'}]
接下来,我们使用defaultdict选项来提取我们的类型和值键
d = defaultdict(list)
for i in out:
d[i['type']].append(i['value'])
print(d)
defaultdict(list,
{'A3': ['', ''],
'A4': ['11AA', '22BB'],
'A6': ['AAB1', 'CCD1'],
'A9': ['', ''],
'A10': ['10.5', '20.5'],
'A11': ['ABC', 'ABC'],
'A12': ['NYR', 'NYR'],
'A13': ['NYR', 'NYR'],
'A14': ['NYR', 'NYR']})
将其读入 dataframe:
df2 = pd.DataFrame(d)
df2
A3 A4 A6 A9 A10 A11 A12 A13 A14
0 11AA AAB1 10.5 ABC NYR NYR NYR
1 22BB CCD1 20.5 ABC NYR NYR NYR
第三部分是提取错误数据:列表的[]
和.
对于键也适用于此; 但是,我们可以在key:value
对中取回我们的数据,例如 dict:
errors = jmespath.compile('[].errors[].{response:response,feedback:feedback}')
err = errors.search(event_records)
print(err)
[{'response': 'NONE', 'feedback': 'Event not found'}]
读入 dataframe:
df3 = pd.DataFrame(err)
df3
response feedback
0 NONE Event not found
我们在最后 - 连接列上的数据框:
result = pd.concat([df1,df2,df3],axis = 1)
old_id new_id event_id event_time value quantity unique_id A3 A4 A6 A9 A10 A11 A12 A13 A14 response feedback
0 123 456 111 1200 1.0 1 222 11AA AAB1 10.5 ABC NYR NYR NYR NONE Event not found
1 567 789 333 1400 1.0 1 444 22BB CCD1 20.5 ABC NYR NYR NYR NaN NaN
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.