![](/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.