[英]Fastest way to match 2 lists of dicts on a key value
我有一個腳本,它從 CSV (~2.5m) 檢索用戶數據並通過 API (~2m) 從 Salesforce 記錄數據,並根據唯一的user_id
匹配它們。
對於每個用戶,我需要相關的record_id
(如果存在) 。 用戶和記錄是一對一的關系,所以user_id
應該只出現在 1 條記錄上。
為了嘗試提高性能,兩個列表都按user_id
升序排序,如果record['user_id'] > user['user_id']
則我打破循環,因為這意味着沒有相關記錄。
它正在工作,但是在嘗試匹配大約 1.5 小時的 2 個數據集時速度很慢。 是否有更快的方法來執行匹配以檢索相關的record_id
?
以下是數據、當前函數和預期結果的示例:
users = [
{"user_id": 11111, "name": "Customer A", "age": 34, 'record_id': None},
{"user_id": 22222, "name": "Customer B", "age": 18, 'record_id': None},
{"user_id": 33333, "name": "Customer C", "age": 66, 'record_id': None}
]
records = [
{"user_id": 11111, "record_id": "ABC123"},
{"user_id": 33333, "record_id": "GHI789"}
]
upload = []
for user in users:
for record in records:
if user['user_id'] == record['user_id']:
user['record_id'] = record['record_id']
records.remove(record)
break
elif record['user_id'] > user['user_id']:
break
if user['record_id']:
upload.append(user)
print(upload)
這輸出:
[
{'user_id': 11111, 'name': 'Customer A', 'age': 34, 'record_id': 'ABC123'},
{'user_id': 33333, 'name': 'Customer C', 'age': 66, 'record_id': 'GHI789'}
]
創建一個字典,將用戶的 id 映射到其對應的字典。 然后,您可以使用for
循環添加相關的record_id
字段。 最后,您可以使用列表推導刪除沒有指定record_id
的條目。
這不需要任何預處理(例如排序)來獲得加速; 效率提升來自於在大字典中查找比搜索大列表更快的事實:
user_id_mapping = {entry["user_id"]: entry for entry in users}
for record in records:
if record["user_id"] in user_id_mapping:
user_id_mapping[record["user_id"]]["record_id"] = record["record_id"]
result = [item for item in user_id_mapping.values() if item["record_id"] is not None]
print(result)
這輸出:
[
{'user_id': 11111, 'name': 'Customer A', 'age': 34, 'record_id': 'ABC123'},
{'user_id': 33333, 'name': 'Customer C', 'age': 66, 'record_id': 'GHI789'}
]
話雖如此,如果您必須重復執行類似的操作,我建議您使用某種數據庫而不是在 Python 中執行此操作。
您可以使用pandas.read_csv()
將 CSV 數據讀入數據框,然后merge
其與user_id
值上的records
合並:
import pandas as pd
users = pd.read_csv('csv file')
records = pd.DataFrame('result of salesforce query')
result = users.drop('record_id', axis=1).merge(records, on='user_id')
如果要保留records
中沒有匹配值的users
,請將合並更改為
merge(records, on='user_id', how='left')
要將結果輸出為字典列表,請使用to_dict()
:
result.to_dict('records')
注意 - 可以將您的 Salesforce 查詢直接執行到數據框中。 例如看這個問答
你的做法不無道理。 但是在使用后刪除record
是有代價的。 提前對兩個列表進行排序也是有代價的。 這些成本加起來可能比你想象的要多。
一種可能的方法是不對列表進行排序,而是構建一個record_ids的字典,例如:
rdict = { r['user_id']:r['record_id'] for r in records }
for user in users:
user_id = user['user_id']
record_id = rdict.get(user_id)
if record_id:
user['record_id'] = record_id
upload.append(user)
這樣,您只需為構建哈希付出一次代價,其他一切都非常有效。
為了可擴展性,您可以使用 pandas 數據框,如下所示:
result = pd.merge(pd.DataFrame(users), pd.DataFrame(records), on='user_id').to_dict('records')
如果要保留沒有record_id
的條目,可以將how="left"
添加到merge
函數的參數中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.